defuss-desktop 0.0.3 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -5,24 +5,261 @@ var defussRuntime = require('defuss-runtime');
5
5
  var jsxRuntime = require('defuss/jsx-runtime');
6
6
  var defussTransval = require('defuss-transval');
7
7
 
8
- function Button({
9
- onClick = () => {
10
- },
11
- disabled = false,
12
- children,
13
- ref = defuss.createRef()
14
- }) {
15
- return /* @__PURE__ */ jsxRuntime.jsx(
16
- "button",
17
- {
18
- ref,
19
- type: "button",
20
- onClick: disabled ? void 0 : onClick,
21
- disabled,
22
- children
8
+ class DesktopShellManager {
9
+ constructor(apps = []) {
10
+ this.apps = apps;
11
+ }
12
+ addApp(app) {
13
+ this.apps.push(app);
14
+ console.log(`App added: ${app.config.name}`);
15
+ }
16
+ async registerAppBundle(bundle) {
17
+ const { DefussApp } = await Promise.resolve().then(function () { return app; });
18
+ return DefussApp.fromBundle(bundle);
19
+ }
20
+ /** Find a registered app by executable name (e.g. "notepad.exe") */
21
+ findApp(executable) {
22
+ return this.apps.find((app) => app.bundle?.executable === executable);
23
+ }
24
+ /** Run an app by executable name */
25
+ runApp(executable) {
26
+ const app = this.findApp(executable);
27
+ if (!app) {
28
+ console.error(`App not found: ${executable}`);
29
+ return;
23
30
  }
24
- );
31
+ app.run();
32
+ }
33
+ /** Launch a bundled app by creating a window and passing the container */
34
+ launchApp(app) {
35
+ if (!app.bundle) return;
36
+ const event = new CustomEvent("defuss:launch-app", { detail: { app } });
37
+ document.dispatchEvent(event);
38
+ }
39
+ }
40
+ globalThis.__defussDesktopShellManager = globalThis.__defussDesktopShellManager || new DesktopShellManager();
41
+ const desktopShell = globalThis.__defussDesktopShellManager;
42
+
43
+ class SelectionModel {
44
+ constructor(options) {
45
+ this.options = options;
46
+ }
47
+ selectionDiv = null;
48
+ startX = 0;
49
+ startY = 0;
50
+ debounceTimer = null;
51
+ isSelecting = false;
52
+ debounceDelay = 1;
53
+ init() {
54
+ this.options.desktopElement.addEventListener("mousedown", this.onMouseDown);
55
+ }
56
+ destroy() {
57
+ this.options.desktopElement.removeEventListener("mousedown", this.onMouseDown);
58
+ this.removeSelectionDiv();
59
+ this.clearDebounce();
60
+ document.removeEventListener("mousemove", this.onMouseMove);
61
+ document.removeEventListener("mouseup", this.onMouseUp);
62
+ document.documentElement.removeEventListener("mouseleave", this.onMouseLeave);
63
+ }
64
+ onMouseDown = (e) => {
65
+ if (e.target.closest(".desktop-icon")) return;
66
+ this.startX = e.clientX;
67
+ this.startY = e.clientY;
68
+ this.isSelecting = true;
69
+ this.createSelectionDiv();
70
+ document.addEventListener("mousemove", this.onMouseMove);
71
+ document.addEventListener("mouseup", this.onMouseUp);
72
+ document.documentElement.addEventListener("mouseleave", this.onMouseLeave);
73
+ };
74
+ onMouseMove = (e) => {
75
+ if (!this.isSelecting) return;
76
+ this.clearDebounce();
77
+ this.debounceTimer = window.setTimeout(() => {
78
+ this.updateSelectionDiv(e.clientX, e.clientY);
79
+ this.selectIcons();
80
+ }, this.debounceDelay);
81
+ };
82
+ onMouseUp = () => {
83
+ this.endSelection();
84
+ };
85
+ onMouseLeave = () => {
86
+ this.endSelection();
87
+ };
88
+ endSelection = () => {
89
+ if (!this.isSelecting) return;
90
+ this.isSelecting = false;
91
+ this.clearDebounce();
92
+ this.selectIcons();
93
+ this.removeSelectionDiv();
94
+ document.removeEventListener("mousemove", this.onMouseMove);
95
+ document.removeEventListener("mouseup", this.onMouseUp);
96
+ document.documentElement.removeEventListener("mouseleave", this.onMouseLeave);
97
+ };
98
+ createSelectionDiv() {
99
+ this.selectionDiv = document.createElement("div");
100
+ this.selectionDiv.className = "selection-model";
101
+ this.selectionDiv.style.left = `${this.startX}px`;
102
+ this.selectionDiv.style.top = `${this.startY}px`;
103
+ this.selectionDiv.style.width = "0px";
104
+ this.selectionDiv.style.height = "0px";
105
+ document.body.appendChild(this.selectionDiv);
106
+ }
107
+ updateSelectionDiv(endX, endY) {
108
+ if (!this.selectionDiv) return;
109
+ const left = Math.min(this.startX, endX);
110
+ const top = Math.min(this.startY, endY);
111
+ const width = Math.abs(endX - this.startX);
112
+ const height = Math.abs(endY - this.startY);
113
+ this.selectionDiv.style.left = `${left}px`;
114
+ this.selectionDiv.style.top = `${top}px`;
115
+ this.selectionDiv.style.width = `${width}px`;
116
+ this.selectionDiv.style.height = `${height}px`;
117
+ }
118
+ selectIcons() {
119
+ const icons = this.options.iconsContainer.querySelectorAll(".desktop-icon");
120
+ if (!this.selectionDiv) return;
121
+ const selRect = this.selectionDiv.getBoundingClientRect();
122
+ icons.forEach((icon) => {
123
+ const iconRect = icon.getBoundingClientRect();
124
+ const intersects = !(iconRect.right < selRect.left || iconRect.left > selRect.right || iconRect.bottom < selRect.top || iconRect.top > selRect.bottom);
125
+ if (intersects) {
126
+ icon.classList.add("icon-selected");
127
+ } else {
128
+ icon.classList.remove("icon-selected");
129
+ }
130
+ });
131
+ }
132
+ removeSelectionDiv() {
133
+ if (this.selectionDiv) {
134
+ this.selectionDiv.remove();
135
+ this.selectionDiv = null;
136
+ }
137
+ }
138
+ clearDebounce() {
139
+ if (this.debounceTimer !== null) {
140
+ clearTimeout(this.debounceTimer);
141
+ this.debounceTimer = null;
142
+ }
143
+ }
144
+ }
145
+
146
+ const defaultTaskbarOptions = {
147
+ position: "bottom",
148
+ stateful: false,
149
+ theme: "default",
150
+ size: "medium"
151
+ };
152
+ class TaskbarManager {
153
+ position;
154
+ theme;
155
+ // e.g., 'windows-xp', 'macos', etc.
156
+ size;
157
+ constructor(options = defaultTaskbarOptions) {
158
+ this.position = options.position || "bottom";
159
+ this.theme = options.theme || "default";
160
+ this.size = options.size || "medium";
161
+ if (options.stateful) ;
162
+ }
163
+ getDimensions() {
164
+ const el = document.querySelector(".taskbar");
165
+ if (!el) {
166
+ return { width: 0, height: 0 };
167
+ }
168
+ return {
169
+ width: el.clientWidth,
170
+ height: el.clientHeight
171
+ };
172
+ }
173
+ }
174
+ globalThis.__defussTaskbarManager = globalThis.__defussTaskbarManager || new TaskbarManager();
175
+ const taskbarManager = globalThis.__defussTaskbarManager;
176
+
177
+ class DefussDesktopAppIcon {
178
+ constructor(config) {
179
+ this.config = config;
180
+ }
181
+ }
182
+ const defaultDesktopOptions = {
183
+ icons: [],
184
+ backgroundColor: "#000"
185
+ };
186
+ class DesktopManager {
187
+ constructor(options = defaultDesktopOptions) {
188
+ this.options = options;
189
+ }
190
+ el;
191
+ state;
192
+ resizeObserver;
193
+ resizeCallbacks = /* @__PURE__ */ new Set();
194
+ init(el, options = this.options) {
195
+ this.options = options;
196
+ this.el = el;
197
+ this.state = this.state || {
198
+ icons: this.options.icons.map((icon) => icon.config)
199
+ };
200
+ this.setupResizeObserver();
201
+ this.render(el);
202
+ }
203
+ render(el) {
204
+ el.style.backgroundColor = this.options.backgroundColor;
205
+ if (this.options.backgroundImage) {
206
+ el.style.backgroundImage = `url(${this.options.backgroundImage})`;
207
+ }
208
+ if (this.options.backgroundImageSize) {
209
+ el.style.backgroundSize = this.options.backgroundImageSize || "cover";
210
+ }
211
+ if (this.options.backgroundRepeat) {
212
+ el.style.backgroundRepeat = this.options.backgroundRepeat || "no-repeat";
213
+ }
214
+ if (this.options.backgroundPosition) {
215
+ el.style.backgroundPosition = this.options.backgroundPosition || "center";
216
+ }
217
+ }
218
+ addIcon(icon) {
219
+ this.options.icons.push(icon);
220
+ console.log(`Icon added: ${icon.config.name}`);
221
+ }
222
+ getDimensions() {
223
+ if (!this.el) {
224
+ throw new Error("Desktop not initialized. Call init() first.");
225
+ }
226
+ return {
227
+ width: this.el.offsetWidth,
228
+ height: this.el.offsetHeight - taskbarManager.getDimensions().height
229
+ // destop is root element minus taskbar height
230
+ };
231
+ }
232
+ setupResizeObserver() {
233
+ if (!this.el) return;
234
+ if (this.resizeObserver) {
235
+ this.resizeObserver.disconnect();
236
+ }
237
+ this.resizeObserver = new ResizeObserver(() => {
238
+ const dimensions = this.getDimensions();
239
+ this.resizeCallbacks.forEach((callback) => {
240
+ try {
241
+ callback(dimensions);
242
+ } catch (error) {
243
+ console.error("Error in desktop resize callback:", error);
244
+ }
245
+ });
246
+ });
247
+ this.resizeObserver.observe(this.el);
248
+ }
249
+ /**
250
+ * Register a callback for desktop resize events
251
+ * @param callback Function to call when desktop is resized
252
+ * @returns Unregister function to remove the callback
253
+ */
254
+ onResize(callback) {
255
+ this.resizeCallbacks.add(callback);
256
+ return () => {
257
+ this.resizeCallbacks.delete(callback);
258
+ };
259
+ }
25
260
  }
261
+ globalThis.__defussDesktopManager = globalThis.__defussDesktopManager || new DesktopManager();
262
+ const desktopManager = globalThis.__defussDesktopManager;
26
263
 
27
264
  const defaultWindowOptions = {
28
265
  title: "Untitled",
@@ -44,6 +281,18 @@ const defaultWindowOptions = {
44
281
  };
45
282
  class WindowManager {
46
283
  windows = [];
284
+ listeners = /* @__PURE__ */ new Set();
285
+ subscribe(listener) {
286
+ this.listeners.add(listener);
287
+ return () => {
288
+ this.listeners.delete(listener);
289
+ };
290
+ }
291
+ emitChanged() {
292
+ for (const listener of this.listeners) {
293
+ listener();
294
+ }
295
+ }
47
296
  constructor() {
48
297
  defuss.$(() => {
49
298
  if (desktopManager?.onResize) {
@@ -90,6 +339,7 @@ class WindowManager {
90
339
  });
91
340
  this.windows.push(window);
92
341
  this.renderWindowsActivationState();
342
+ this.emitChanged();
93
343
  }
94
344
  }
95
345
  renderWindowsActivationState() {
@@ -139,6 +389,7 @@ class WindowManager {
139
389
  state.y = activeWindow.y + 20;
140
390
  }
141
391
  this.windows.push(state);
392
+ this.emitChanged();
142
393
  return state;
143
394
  }
144
395
  updateWindow(id, options) {
@@ -162,6 +413,7 @@ class WindowManager {
162
413
  ...updatedWindow
163
414
  } : win2
164
415
  );
416
+ this.emitChanged();
165
417
  return updatedWindow;
166
418
  }
167
419
  closeWindow(id) {
@@ -170,6 +422,7 @@ class WindowManager {
170
422
  defuss.$(win.el).remove();
171
423
  this.windows = this.windows.filter((win2) => win2.id !== id);
172
424
  this.renderWindowsActivationState();
425
+ this.emitChanged();
173
426
  win.ref.state?.onClose?.();
174
427
  }
175
428
  maximizeWindow(id) {
@@ -211,6 +464,7 @@ class WindowManager {
211
464
  top: isMaximized ? "-3px" : `${win.y}px`
212
465
  });
213
466
  this.toggleTitleBarMaximizedButtonState(id);
467
+ this.emitChanged();
214
468
  if (isMaximized) {
215
469
  win.ref.state?.onMaximize?.();
216
470
  }
@@ -218,31 +472,33 @@ class WindowManager {
218
472
  minimizeWindow(id) {
219
473
  let win = this.getWindow(id);
220
474
  if (!win) return;
221
- win = this.updateWindow(id, { prevX: win.x, prevY: win.y });
222
- const isMinimized = !win.minimized;
475
+ if (win.minimized) {
476
+ this.restoreWindow(id);
477
+ return;
478
+ }
479
+ const actualWidth = win.el.offsetWidth || win.width;
480
+ const actualHeight = win.el.offsetHeight || win.height;
223
481
  win = this.updateWindow(id, {
224
- minimized: isMinimized,
225
- width: isMinimized ? 0 : win.width,
226
- height: isMinimized ? 0 : win.height,
227
- x: isMinimized ? -1e4 : win.x,
228
- // Move off-screen when minimized
229
- y: isMinimized ? -1e4 : win.y
482
+ prevX: win.x,
483
+ prevY: win.y,
484
+ prevWidth: actualWidth,
485
+ prevHeight: actualHeight,
486
+ minimized: true
230
487
  });
231
488
  const $win = defuss.$(win.el);
232
489
  $win.css({
233
- width: isMinimized ? "0px" : `${win.width}px`,
234
- height: isMinimized ? "0px" : `${win.height}px`,
235
- left: isMinimized ? "-10000px" : `${win.x}px`,
236
- top: isMinimized ? "-10000px" : `${win.y}px`
490
+ left: "-10000px",
491
+ top: "-10000px"
237
492
  });
238
- if (isMinimized) {
239
- win.ref.state?.onMinimize?.();
240
- }
493
+ this.renderWindowsActivationState();
494
+ this.emitChanged();
495
+ win.ref.state?.onMinimize?.();
241
496
  }
242
497
  restoreWindow(id) {
243
498
  let win = this.getWindow(id);
244
499
  if (!win) return;
245
500
  win = this.updateWindow(id, {
501
+ minimized: false,
246
502
  maximized: false,
247
503
  width: win.prevWidth || 800,
248
504
  // Use stored previous width
@@ -259,6 +515,8 @@ class WindowManager {
259
515
  top: `${win.y}px`
260
516
  });
261
517
  this.toggleTitleBarMaximizedButtonState(id);
518
+ this.setActiveWindow(id);
519
+ this.emitChanged();
262
520
  }
263
521
  toggleTitleBarMaximizedButtonState(id) {
264
522
  const win = this.getWindow(id);
@@ -291,12 +549,20 @@ function Window({
291
549
  onMaximize = () => {
292
550
  }
293
551
  }) {
552
+ const MIN_WINDOW_WIDTH = 240;
553
+ const MIN_WINDOW_HEIGHT = 160;
294
554
  let isDragging = false;
295
555
  let dragPointerId = null;
296
556
  let dragStartMouse = { x: 0, y: 0 };
297
557
  let dragStartWin = { x: 0, y: 0 };
298
558
  let lastWin = { x, y };
299
559
  let capturedTitleBar = null;
560
+ let isResizing = false;
561
+ let resizePointerId = null;
562
+ let resizeDirection = null;
563
+ let capturedResizeHandle = null;
564
+ let resizeStartMouse = { x: 0, y: 0 };
565
+ let resizeStartWin = { x, y, width, height };
300
566
  const initialWindowState = windowManager.addWindow({
301
567
  id,
302
568
  title,
@@ -360,7 +626,33 @@ function Window({
360
626
  capturedTitleBar = null;
361
627
  windowManager.updateWindow(initialWindowState.id, { x: lastWin.x, y: lastWin.y });
362
628
  };
629
+ const stopResizing = (event) => {
630
+ if (!isResizing) return;
631
+ if (event && capturedResizeHandle && typeof capturedResizeHandle.releasePointerCapture === "function" && resizePointerId === event.pointerId) {
632
+ try {
633
+ capturedResizeHandle.releasePointerCapture(event.pointerId);
634
+ } catch {
635
+ }
636
+ }
637
+ isResizing = false;
638
+ resizePointerId = null;
639
+ resizeDirection = null;
640
+ capturedResizeHandle = null;
641
+ const el = ref.current;
642
+ if (!el) return;
643
+ const left = Number.parseFloat(el.style.left);
644
+ const top = Number.parseFloat(el.style.top);
645
+ const width2 = Number.parseFloat(el.style.width);
646
+ const height2 = Number.parseFloat(el.style.height);
647
+ windowManager.updateWindow(initialWindowState.id, {
648
+ x: Number.isFinite(left) ? left : lastWin.x,
649
+ y: Number.isFinite(top) ? top : lastWin.y,
650
+ width: Number.isFinite(width2) ? width2 : el.offsetWidth,
651
+ height: Number.isFinite(height2) ? height2 : el.offsetHeight
652
+ });
653
+ };
363
654
  const onTitlePointerDown = (event) => {
655
+ if (isResizing) return;
364
656
  if (event.pointerType === "mouse" && event.button !== 0) return;
365
657
  const target = event.target;
366
658
  if (target?.tagName === "BUTTON") return;
@@ -377,6 +669,85 @@ function Window({
377
669
  }
378
670
  event.preventDefault();
379
671
  };
672
+ const onResizePointerDown = (event) => {
673
+ if (!resizable) return;
674
+ if (isDragging) return;
675
+ if (event.pointerType === "mouse" && event.button !== 0) return;
676
+ const currentState = windowManager.getWindow(initialWindowState.id);
677
+ if (currentState?.maximized) return;
678
+ const target = event.target;
679
+ const handle = target?.closest?.(".window-resize-handle");
680
+ const direction = handle?.getAttribute("data-dir");
681
+ if (!handle || !direction) return;
682
+ const currentPos = getCurrentWinPos();
683
+ const el = ref.current;
684
+ if (!el) return;
685
+ isResizing = true;
686
+ resizePointerId = event.pointerId;
687
+ resizeDirection = direction;
688
+ capturedResizeHandle = handle;
689
+ resizeStartMouse = { x: event.clientX, y: event.clientY };
690
+ resizeStartWin = {
691
+ x: currentPos.x,
692
+ y: currentPos.y,
693
+ width: el.offsetWidth,
694
+ height: el.offsetHeight
695
+ };
696
+ if (typeof handle.setPointerCapture === "function") {
697
+ handle.setPointerCapture(event.pointerId);
698
+ }
699
+ windowManager.setActiveWindow(initialWindowState.id);
700
+ event.preventDefault();
701
+ event.stopPropagation();
702
+ };
703
+ const onResizePointerMove = (event) => {
704
+ if (!isResizing) return;
705
+ if (resizePointerId !== null && event.pointerId !== resizePointerId) return;
706
+ if (!resizeDirection) return;
707
+ const el = ref.current;
708
+ if (!el) return;
709
+ const deltaX = event.clientX - resizeStartMouse.x;
710
+ const deltaY = event.clientY - resizeStartMouse.y;
711
+ let newX = resizeStartWin.x;
712
+ let newY = resizeStartWin.y;
713
+ let newWidth = resizeStartWin.width;
714
+ let newHeight = resizeStartWin.height;
715
+ if (resizeDirection.includes("e")) {
716
+ newWidth = Math.max(MIN_WINDOW_WIDTH, resizeStartWin.width + deltaX);
717
+ }
718
+ if (resizeDirection.includes("s")) {
719
+ newHeight = Math.max(MIN_WINDOW_HEIGHT, resizeStartWin.height + deltaY);
720
+ }
721
+ if (resizeDirection.includes("w")) {
722
+ const tentativeWidth = resizeStartWin.width - deltaX;
723
+ newWidth = Math.max(MIN_WINDOW_WIDTH, tentativeWidth);
724
+ newX = resizeStartWin.x + (resizeStartWin.width - newWidth);
725
+ }
726
+ if (resizeDirection.includes("n")) {
727
+ const tentativeHeight = resizeStartWin.height - deltaY;
728
+ newHeight = Math.max(MIN_WINDOW_HEIGHT, tentativeHeight);
729
+ newY = resizeStartWin.y + (resizeStartWin.height - newHeight);
730
+ }
731
+ lastWin = { x: newX, y: newY };
732
+ el.style.left = `${newX}px`;
733
+ el.style.top = `${newY}px`;
734
+ el.style.width = `${newWidth}px`;
735
+ el.style.height = `${newHeight}px`;
736
+ updateWindowState({
737
+ x: newX,
738
+ y: newY,
739
+ width: newWidth,
740
+ height: newHeight
741
+ });
742
+ event.preventDefault();
743
+ event.stopPropagation();
744
+ };
745
+ const onResizePointerUp = (event) => {
746
+ stopResizing(event);
747
+ };
748
+ const onResizePointerCancel = (event) => {
749
+ stopResizing(event);
750
+ };
380
751
  const onTitlePointerMove = (event) => {
381
752
  if (!isDragging) return;
382
753
  if (dragPointerId !== null && event.pointerId !== dragPointerId) return;
@@ -405,7 +776,10 @@ function Window({
405
776
  windowManager.setActiveWindow(initialWindowState.id);
406
777
  defuss.$(document).on("blur", () => stopDragging());
407
778
  defuss.$(document).on("visibilitychange", () => {
408
- if (document.hidden) stopDragging();
779
+ if (document.hidden) {
780
+ stopDragging();
781
+ stopResizing();
782
+ }
409
783
  });
410
784
  };
411
785
  const onCloseClick = () => windowManager.closeWindow(initialWindowState.id);
@@ -422,6 +796,7 @@ function Window({
422
796
  {
423
797
  class: "window crt",
424
798
  ref,
799
+ "data-resizable": String(resizable),
425
800
  onMouseDown: onWindowMouseDown,
426
801
  style: {
427
802
  width,
@@ -452,68 +827,230 @@ function Window({
452
827
  ]
453
828
  }
454
829
  ),
455
- /* @__PURE__ */ jsxRuntime.jsx("div", { class: "window-body", children })
830
+ /* @__PURE__ */ jsxRuntime.jsx("div", { class: "window-body", children }),
831
+ resizable && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
832
+ /* @__PURE__ */ jsxRuntime.jsx(
833
+ "div",
834
+ {
835
+ class: "window-resize-handle window-resize-handle--n",
836
+ "data-dir": "n",
837
+ onPointerDown: onResizePointerDown,
838
+ onPointerMove: onResizePointerMove,
839
+ onPointerUp: onResizePointerUp,
840
+ onPointerCancel: onResizePointerCancel
841
+ }
842
+ ),
843
+ /* @__PURE__ */ jsxRuntime.jsx(
844
+ "div",
845
+ {
846
+ class: "window-resize-handle window-resize-handle--e",
847
+ "data-dir": "e",
848
+ onPointerDown: onResizePointerDown,
849
+ onPointerMove: onResizePointerMove,
850
+ onPointerUp: onResizePointerUp,
851
+ onPointerCancel: onResizePointerCancel
852
+ }
853
+ ),
854
+ /* @__PURE__ */ jsxRuntime.jsx(
855
+ "div",
856
+ {
857
+ class: "window-resize-handle window-resize-handle--s",
858
+ "data-dir": "s",
859
+ onPointerDown: onResizePointerDown,
860
+ onPointerMove: onResizePointerMove,
861
+ onPointerUp: onResizePointerUp,
862
+ onPointerCancel: onResizePointerCancel
863
+ }
864
+ ),
865
+ /* @__PURE__ */ jsxRuntime.jsx(
866
+ "div",
867
+ {
868
+ class: "window-resize-handle window-resize-handle--w",
869
+ "data-dir": "w",
870
+ onPointerDown: onResizePointerDown,
871
+ onPointerMove: onResizePointerMove,
872
+ onPointerUp: onResizePointerUp,
873
+ onPointerCancel: onResizePointerCancel
874
+ }
875
+ ),
876
+ /* @__PURE__ */ jsxRuntime.jsx(
877
+ "div",
878
+ {
879
+ class: "window-resize-handle window-resize-handle--ne",
880
+ "data-dir": "ne",
881
+ onPointerDown: onResizePointerDown,
882
+ onPointerMove: onResizePointerMove,
883
+ onPointerUp: onResizePointerUp,
884
+ onPointerCancel: onResizePointerCancel
885
+ }
886
+ ),
887
+ /* @__PURE__ */ jsxRuntime.jsx(
888
+ "div",
889
+ {
890
+ class: "window-resize-handle window-resize-handle--se",
891
+ "data-dir": "se",
892
+ onPointerDown: onResizePointerDown,
893
+ onPointerMove: onResizePointerMove,
894
+ onPointerUp: onResizePointerUp,
895
+ onPointerCancel: onResizePointerCancel
896
+ }
897
+ ),
898
+ /* @__PURE__ */ jsxRuntime.jsx(
899
+ "div",
900
+ {
901
+ class: "window-resize-handle window-resize-handle--sw",
902
+ "data-dir": "sw",
903
+ onPointerDown: onResizePointerDown,
904
+ onPointerMove: onResizePointerMove,
905
+ onPointerUp: onResizePointerUp,
906
+ onPointerCancel: onResizePointerCancel
907
+ }
908
+ ),
909
+ /* @__PURE__ */ jsxRuntime.jsx(
910
+ "div",
911
+ {
912
+ class: "window-resize-handle window-resize-handle--nw",
913
+ "data-dir": "nw",
914
+ onPointerDown: onResizePointerDown,
915
+ onPointerMove: onResizePointerMove,
916
+ onPointerUp: onResizePointerUp,
917
+ onPointerCancel: onResizePointerCancel
918
+ }
919
+ )
920
+ ] })
456
921
  ]
457
922
  }
458
923
  );
459
924
  }
460
925
 
926
+ function DesktopIcon({ app }) {
927
+ const bundle = app.bundle;
928
+ const iconSrc = bundle?.icon ?? app.config.icon;
929
+ const label = bundle?.displayName ?? app.config.name;
930
+ const onClick = (e) => {
931
+ if (e.detail > 1) return;
932
+ const allIcons = document.querySelectorAll(".desktop-icon");
933
+ allIcons.forEach((icon) => icon.classList.remove("icon-selected"));
934
+ const target = e.target.closest(".desktop-icon");
935
+ if (target) {
936
+ target.classList.add("icon-selected");
937
+ }
938
+ };
939
+ const onDblClick = () => {
940
+ app.run();
941
+ };
942
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { class: "desktop-icon", onClick, onDblClick, children: [
943
+ /* @__PURE__ */ jsxRuntime.jsx("div", { class: "desktop-icon__image", style: { "--icon-src": `url(${iconSrc})` }, children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: iconSrc, alt: label, draggable: false }) }),
944
+ /* @__PURE__ */ jsxRuntime.jsx("span", { class: "desktop-icon__label", children: label })
945
+ ] }, bundle?.executable ?? label);
946
+ }
947
+
461
948
  function Desktop({ ref }) {
462
- defuss.$(() => {
463
- console.log("Desktop mounted");
464
- });
465
- const onOpenWindow = async () => {
949
+ const appIconsRef = defuss.createRef();
950
+ const onLaunchApp = (event) => {
951
+ const customEvent = event;
952
+ const app = customEvent.detail?.app;
953
+ if (!app?.bundle) return;
954
+ void runBundledApp(app);
955
+ };
956
+ const resolveAppMain = async (app) => {
957
+ const bundle = app.bundle;
958
+ if (!bundle) return void 0;
959
+ if (bundle.main) {
960
+ return bundle.main;
961
+ }
962
+ if (bundle.load) {
963
+ const loaded = await bundle.load();
964
+ return loaded.main;
965
+ }
966
+ return void 0;
967
+ };
968
+ const runBundledApp = async (app) => {
969
+ const bundle = app.bundle;
970
+ if (!bundle) return;
971
+ const main = await resolveAppMain(app);
972
+ if (!main) {
973
+ console.error(`No app main() available for ${bundle.executable}`);
974
+ return;
975
+ }
976
+ const appRootRef = defuss.createRef();
466
977
  const winRef = defuss.createRef();
467
978
  await defuss.$(ref).append(
468
- /* @__PURE__ */ jsxRuntime.jsxs(
979
+ /* @__PURE__ */ jsxRuntime.jsx(
469
980
  Window,
470
981
  {
471
- width: 300,
472
- height: 200,
473
- title: "Test Window",
982
+ width: bundle.width ?? 720,
983
+ height: bundle.height ?? 480,
984
+ title: bundle.displayName,
985
+ id: bundle.executable,
474
986
  ref: winRef,
475
987
  onClose: () => {
476
- console.log("I WAS CLOSED!");
477
- },
478
- onMaximize: () => {
479
- console.log("I WAS MAXIMIZED!");
988
+ console.log(`${bundle.executable} was closed`);
480
989
  },
481
- onMinimize: () => {
482
- console.log("I WAS MINIMIZED!");
483
- },
484
- children: [
485
- /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Hello, world!" }),
486
- /* @__PURE__ */ jsxRuntime.jsxs("section", { class: "field-row", style: "justify-content: space-between;", children: [
487
- /* @__PURE__ */ jsxRuntime.jsx(
488
- "button",
489
- {
490
- type: "button",
491
- onClick: () => {
492
- console.log("Cancel clicked");
493
- winRef.state?.close();
494
- },
495
- children: "Cancel"
496
- }
497
- ),
498
- /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: onOpenWindow, children: "Open Window" }),
499
- /* @__PURE__ */ jsxRuntime.jsx(
500
- "button",
501
- {
502
- type: "button",
503
- onClick: () => {
504
- console.log("OK clicked");
505
- winRef.state?.close();
506
- },
507
- children: "OK"
508
- }
509
- )
510
- ] })
511
- ]
990
+ children: /* @__PURE__ */ jsxRuntime.jsx(
991
+ "div",
992
+ {
993
+ class: "defuss-app-root",
994
+ ref: appRootRef,
995
+ onMount: () => {
996
+ void main({ app, container: appRootRef.current });
997
+ }
998
+ }
999
+ )
512
1000
  }
513
1001
  )
514
1002
  );
515
1003
  };
516
- return /* @__PURE__ */ jsxRuntime.jsx("div", { class: "defuss-desktop-panel crt", ref, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: onOpenWindow, children: "Open Window" }) });
1004
+ const renderDesktopIcons = async () => {
1005
+ const appIcons = desktopShell.apps.filter((app) => app.bundle);
1006
+ await defuss.$(appIconsRef).update(
1007
+ /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: appIcons.map((app) => /* @__PURE__ */ jsxRuntime.jsx(DesktopIcon, { app }, app.bundle.executable)) })
1008
+ );
1009
+ };
1010
+ let selectionModel = null;
1011
+ const onMountDesktop = () => {
1012
+ void renderDesktopIcons();
1013
+ document.addEventListener("defuss:launch-app", onLaunchApp);
1014
+ selectionModel = new SelectionModel({
1015
+ desktopElement: ref.current,
1016
+ iconsContainer: appIconsRef.current
1017
+ });
1018
+ selectionModel.init();
1019
+ };
1020
+ const onUnmountDesktop = () => {
1021
+ document.removeEventListener("defuss:launch-app", onLaunchApp);
1022
+ selectionModel?.destroy();
1023
+ selectionModel = null;
1024
+ };
1025
+ return /* @__PURE__ */ jsxRuntime.jsx(
1026
+ "div",
1027
+ {
1028
+ class: "defuss-desktop-panel crt",
1029
+ ref,
1030
+ onMount: onMountDesktop,
1031
+ onUnmount: onUnmountDesktop,
1032
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { class: "desktop-icons-grid", ref: appIconsRef })
1033
+ }
1034
+ );
1035
+ }
1036
+
1037
+ function Button({
1038
+ onClick = () => {
1039
+ },
1040
+ disabled = false,
1041
+ children,
1042
+ ref = defuss.createRef()
1043
+ }) {
1044
+ return /* @__PURE__ */ jsxRuntime.jsx(
1045
+ "button",
1046
+ {
1047
+ ref,
1048
+ type: "button",
1049
+ onClick: disabled ? void 0 : onClick,
1050
+ disabled,
1051
+ children
1052
+ }
1053
+ );
517
1054
  }
518
1055
 
519
1056
  const LogonScreen = ({
@@ -633,146 +1170,67 @@ const LogonScreen = ({
633
1170
  ] });
634
1171
  };
635
1172
 
636
- class DefussDesktopAppIcon {
637
- constructor(config) {
638
- this.config = config;
639
- }
640
- }
641
- const defaultDesktopOptions = {
642
- icons: [],
643
- backgroundColor: "#000"
644
- };
645
- class DesktopManager {
646
- constructor(options = defaultDesktopOptions) {
647
- this.options = options;
648
- }
649
- el;
650
- state;
651
- resizeObserver;
652
- resizeCallbacks = /* @__PURE__ */ new Set();
653
- init(el, options = this.options) {
654
- this.options = options;
655
- this.el = el;
656
- this.state = this.state || {
657
- icons: this.options.icons.map((icon) => icon.config)
658
- };
659
- this.setupResizeObserver();
660
- this.render(el);
661
- }
662
- render(el) {
663
- el.style.backgroundColor = this.options.backgroundColor;
664
- if (this.options.backgroundImage) {
665
- el.style.backgroundImage = `url(${this.options.backgroundImage})`;
666
- }
667
- if (this.options.backgroundImageSize) {
668
- el.style.backgroundSize = this.options.backgroundImageSize || "cover";
669
- }
670
- if (this.options.backgroundRepeat) {
671
- el.style.backgroundRepeat = this.options.backgroundRepeat || "no-repeat";
672
- }
673
- if (this.options.backgroundPosition) {
674
- el.style.backgroundPosition = this.options.backgroundPosition || "center";
675
- }
676
- }
677
- addIcon(icon) {
678
- this.options.icons.push(icon);
679
- console.log(`Icon added: ${icon.config.name}`);
680
- }
681
- getDimensions() {
682
- if (!this.el) {
683
- throw new Error("Desktop not initialized. Call init() first.");
684
- }
685
- return {
686
- width: this.el.offsetWidth,
687
- height: this.el.offsetHeight - taskbarManager.getDimensions().height
688
- // destop is root element minus taskbar height
689
- };
690
- }
691
- setupResizeObserver() {
692
- if (!this.el) return;
693
- if (this.resizeObserver) {
694
- this.resizeObserver.disconnect();
695
- }
696
- this.resizeObserver = new ResizeObserver(() => {
697
- const dimensions = this.getDimensions();
698
- this.resizeCallbacks.forEach((callback) => {
699
- try {
700
- callback(dimensions);
701
- } catch (error) {
702
- console.error("Error in desktop resize callback:", error);
703
- }
704
- });
705
- });
706
- this.resizeObserver.observe(this.el);
707
- }
708
- /**
709
- * Register a callback for desktop resize events
710
- * @param callback Function to call when desktop is resized
711
- * @returns Unregister function to remove the callback
712
- */
713
- onResize(callback) {
714
- this.resizeCallbacks.add(callback);
715
- return () => {
716
- this.resizeCallbacks.delete(callback);
717
- };
718
- }
719
- }
720
- globalThis.__defussDesktopManager = globalThis.__defussDesktopManager || new DesktopManager();
721
- const desktopManager = globalThis.__defussDesktopManager;
722
-
723
1173
  const StartMenu = () => {
1174
+ const bundledApps = desktopShell.apps.filter((app) => app.bundle);
724
1175
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { class: "slide-open crt", children: [
725
1176
  /* @__PURE__ */ jsxRuntime.jsx("div", { class: "top", children: /* @__PURE__ */ jsxRuntime.jsx("h1", { children: "Aron Homberg" }) }),
726
1177
  /* @__PURE__ */ jsxRuntime.jsxs("div", { class: "menu", children: [
727
- /* @__PURE__ */ jsxRuntime.jsx("div", { class: "programs", children: /* @__PURE__ */ jsxRuntime.jsxs("ul", { children: [
728
- /* @__PURE__ */ jsxRuntime.jsxs("li", { tabindex: "-1", children: [
729
- /* @__PURE__ */ jsxRuntime.jsx("a", { class: "program-image", href: "#", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/icons/ie.png", alt: "" }) }),
730
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", children: "Internet Explorer" })
731
- ] }),
732
- /* @__PURE__ */ jsxRuntime.jsxs("li", { tabindex: "-1", children: [
733
- /* @__PURE__ */ jsxRuntime.jsx("a", { class: "program-image", href: "#", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/icons/paint.png", alt: "" }) }),
734
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", children: "Paint" })
735
- ] }),
736
- /* @__PURE__ */ jsxRuntime.jsxs("li", { tabindex: "-1", children: [
737
- /* @__PURE__ */ jsxRuntime.jsx("a", { class: "program-image", href: "#", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/icons/media-player.png", alt: "" }) }),
738
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", children: "Windows Media Player" })
739
- ] }),
740
- /* @__PURE__ */ jsxRuntime.jsxs("li", { tabindex: "-1", children: [
741
- /* @__PURE__ */ jsxRuntime.jsx("a", { class: "program-image", href: "#", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/icons/outlook-express.jpg", alt: "" }) }),
742
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", children: "E-mail" })
743
- ] })
744
- ] }) }),
1178
+ /* @__PURE__ */ jsxRuntime.jsx("div", { class: "programs", children: /* @__PURE__ */ jsxRuntime.jsx("ul", { children: bundledApps.map((app) => /* @__PURE__ */ jsxRuntime.jsxs("li", { tabindex: "-1", children: [
1179
+ /* @__PURE__ */ jsxRuntime.jsx(
1180
+ "a",
1181
+ {
1182
+ class: "program-image",
1183
+ href: "#",
1184
+ onClick: (event) => {
1185
+ event.preventDefault();
1186
+ desktopShell.runApp(app.bundle.executable);
1187
+ },
1188
+ children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: app.bundle.icon, alt: "" })
1189
+ }
1190
+ ),
1191
+ /* @__PURE__ */ jsxRuntime.jsx(
1192
+ "a",
1193
+ {
1194
+ href: "#",
1195
+ onClick: (event) => {
1196
+ event.preventDefault();
1197
+ desktopShell.runApp(app.bundle.executable);
1198
+ },
1199
+ children: app.bundle.displayName
1200
+ }
1201
+ )
1202
+ ] }, app.bundle.executable)) }) }),
745
1203
  /* @__PURE__ */ jsxRuntime.jsx("div", { class: "system", children: /* @__PURE__ */ jsxRuntime.jsxs("ul", { children: [
746
1204
  /* @__PURE__ */ jsxRuntime.jsxs("li", { tabindex: "-1", children: [
747
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", class: "program-image", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/icons/documents.png", alt: "" }) }),
1205
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", class: "program-image", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/desktop/documents.png", alt: "" }) }),
748
1206
  /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", children: "My Documents" })
749
1207
  ] }),
750
1208
  /* @__PURE__ */ jsxRuntime.jsxs("li", { tabindex: "-1", children: [
751
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", class: "program-image", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/icons/pictures.png", alt: "" }) }),
1209
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", class: "program-image", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/desktop/pictures.png", alt: "" }) }),
752
1210
  /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", children: "My Pictures" })
753
1211
  ] }),
754
1212
  /* @__PURE__ */ jsxRuntime.jsxs("li", { tabindex: "-1", children: [
755
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", class: "program-image", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/icons/music.png", alt: "" }) }),
1213
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", class: "program-image", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/desktop/music.png", alt: "" }) }),
756
1214
  /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", children: "My Music" })
757
1215
  ] }),
758
1216
  /* @__PURE__ */ jsxRuntime.jsxs("li", { tabindex: "-1", children: [
759
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", class: "program-image", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/icons/computer.png", alt: "" }) }),
1217
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", class: "program-image", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/desktop/computer.png", alt: "" }) }),
760
1218
  /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", children: "My Computer" })
761
1219
  ] }),
762
1220
  /* @__PURE__ */ jsxRuntime.jsxs("li", { tabindex: "-1", children: [
763
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", class: "program-image", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/icons/control-panel.png", alt: "" }) }),
1221
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", class: "program-image", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/desktop/control-panel.png", alt: "" }) }),
764
1222
  /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", children: "Control Panel" })
765
1223
  ] }),
766
1224
  /* @__PURE__ */ jsxRuntime.jsxs("li", { tabindex: "-1", children: [
767
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", class: "program-image", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/icons/support-help.png", alt: "" }) }),
1225
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", class: "program-image", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/desktop/support-help.png", alt: "" }) }),
768
1226
  /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", children: "Help and Support" })
769
1227
  ] }),
770
1228
  /* @__PURE__ */ jsxRuntime.jsxs("li", { tabindex: "-1", children: [
771
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", class: "program-image", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/icons/search.png", alt: "" }) }),
1229
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", class: "program-image", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/desktop/search.png", alt: "" }) }),
772
1230
  /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", children: "Search" })
773
1231
  ] }),
774
1232
  /* @__PURE__ */ jsxRuntime.jsxs("li", { tabindex: "-1", children: [
775
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", class: "program-image", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/icons/run.png", alt: "" }) }),
1233
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", class: "program-image", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: "/desktop/run.png", alt: "" }) }),
776
1234
  /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#", children: "Run..." })
777
1235
  ] })
778
1236
  ] }) })
@@ -797,11 +1255,63 @@ const StartButton = () => {
797
1255
  };
798
1256
 
799
1257
  const Taskbar = () => {
800
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { class: "bar crt", children: [
1258
+ const taskbarListRef = defuss.createRef();
1259
+ const clockRef = defuss.createRef();
1260
+ const renderTasks = async () => {
1261
+ const windows = [...windowManager.windows];
1262
+ const activeWindow = windowManager.getActiveWindow();
1263
+ await defuss.$(taskbarListRef).update(
1264
+ /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: windows.map((win) => /* @__PURE__ */ jsxRuntime.jsx("li", { class: activeWindow?.id === win.id && !win.minimized ? "active" : "", children: /* @__PURE__ */ jsxRuntime.jsx(
1265
+ "button",
1266
+ {
1267
+ type: "button",
1268
+ class: "taskbar__task-btn",
1269
+ onClick: () => {
1270
+ const current = windowManager.getWindow(win.id);
1271
+ if (!current) return;
1272
+ if (current.minimized) {
1273
+ windowManager.restoreWindow(current.id);
1274
+ return;
1275
+ }
1276
+ const active = windowManager.getActiveWindow();
1277
+ if (active?.id === current.id) {
1278
+ windowManager.minimizeWindow(current.id);
1279
+ return;
1280
+ }
1281
+ windowManager.setActiveWindow(current.id);
1282
+ },
1283
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { class: "cell", children: [
1284
+ win.icon ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: win.icon, alt: "" }) : null,
1285
+ /* @__PURE__ */ jsxRuntime.jsx("span", { class: "cell-name", children: win.title })
1286
+ ] })
1287
+ }
1288
+ ) }, win.id)) })
1289
+ );
1290
+ };
1291
+ const renderClock = () => {
1292
+ const now = /* @__PURE__ */ new Date();
1293
+ const value = now.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" });
1294
+ defuss.$(clockRef).text(value);
1295
+ };
1296
+ const onMount = () => {
1297
+ void renderTasks();
1298
+ renderClock();
1299
+ const unsubscribe = windowManager.subscribe(() => {
1300
+ void renderTasks();
1301
+ });
1302
+ const interval = setInterval(renderClock, 3e4);
1303
+ clockRef.state = { unsubscribe, interval };
1304
+ };
1305
+ const onUnmount = () => {
1306
+ const state = clockRef.state;
1307
+ state?.unsubscribe?.();
1308
+ if (state?.interval) clearInterval(state.interval);
1309
+ };
1310
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { class: "bar crt", onMount, onUnmount, children: [
801
1311
  /* @__PURE__ */ jsxRuntime.jsx(StartButton, {}),
802
- /* @__PURE__ */ jsxRuntime.jsx("ul", { class: "taskbar" }),
1312
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { class: "taskbar", ref: taskbarListRef }),
803
1313
  /* @__PURE__ */ jsxRuntime.jsx("div", { class: "tray-toggle", children: /* @__PURE__ */ jsxRuntime.jsx("div", { class: "arrow" }) }),
804
- /* @__PURE__ */ jsxRuntime.jsx("div", { class: "taskbar__clock", children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: "7:02 AM" }) })
1314
+ /* @__PURE__ */ jsxRuntime.jsx("div", { class: "taskbar__clock", children: /* @__PURE__ */ jsxRuntime.jsx("span", { ref: clockRef, children: "--:--" }) })
805
1315
  ] });
806
1316
  };
807
1317
 
@@ -823,29 +1333,37 @@ function Shell({
823
1333
  ] });
824
1334
  }
825
1335
 
826
- class DesktopShellManager {
827
- constructor(apps = []) {
828
- this.apps = apps;
829
- }
830
- addApp(app) {
831
- this.apps.push(app);
832
- console.log(`App added: ${app.config.name}`);
833
- }
834
- }
835
- globalThis.__defussDesktopShellManager = globalThis.__defussDesktopShellManager || new DesktopShellManager();
836
- const desktopShell = globalThis.__defussDesktopShellManager;
837
-
838
1336
  class DefussApp {
839
1337
  constructor(config) {
840
1338
  this.config = config;
841
1339
  desktopShell.addApp(this);
842
1340
  }
1341
+ bundle;
1342
+ static fromBundle(bundle) {
1343
+ const app = new DefussApp({
1344
+ name: bundle.displayName,
1345
+ icon: bundle.icon,
1346
+ main: () => {
1347
+ }
1348
+ });
1349
+ app.bundle = bundle;
1350
+ return app;
1351
+ }
843
1352
  run() {
844
1353
  console.log(`Running app: ${this.config.name}`);
845
- this.config.main(this, ...this.config.argv || []);
1354
+ if (this.bundle) {
1355
+ desktopShell.launchApp(this);
1356
+ } else {
1357
+ this.config.main(this, ...this.config.argv || []);
1358
+ }
846
1359
  }
847
1360
  }
848
1361
 
1362
+ var app = /*#__PURE__*/Object.freeze({
1363
+ __proto__: null,
1364
+ DefussApp: DefussApp
1365
+ });
1366
+
849
1367
  class DequeryWithWindowManager extends defuss.CallChainImpl {
850
1368
  /*
851
1369
  // create a window from any element (not necessarily identifier as an App)
@@ -857,15 +1375,12 @@ class DequeryWithWindowManager extends defuss.CallChainImpl {
857
1375
  */
858
1376
  // register an app to the desktop shell
859
1377
  createDesktopApp(options) {
860
- return defuss.createCall(this, "createDesktopApp", async () => {
861
- return new DefussApp(options);
862
- });
1378
+ new DefussApp(options);
1379
+ return this;
863
1380
  }
864
1381
  // create a desktop app icon
865
1382
  createDesktopAppIcon(options) {
866
- return defuss.createCall(this, "createDesktopAppIcon", async () => {
867
- return new DefussDesktopAppIcon(options);
868
- });
1383
+ return this;
869
1384
  }
870
1385
  }
871
1386
  const $ = defuss.dequery.extend(DequeryWithWindowManager, [
@@ -874,37 +1389,6 @@ const $ = defuss.dequery.extend(DequeryWithWindowManager, [
874
1389
  "createDesktopAppIcon"
875
1390
  ]);
876
1391
 
877
- const defaultTaskbarOptions = {
878
- position: "bottom",
879
- stateful: false,
880
- theme: "default",
881
- size: "medium"
882
- };
883
- class TaskbarManager {
884
- position;
885
- theme;
886
- // e.g., 'windows-xp', 'macos', etc.
887
- size;
888
- constructor(options = defaultTaskbarOptions) {
889
- this.position = options.position || "bottom";
890
- this.theme = options.theme || "default";
891
- this.size = options.size || "medium";
892
- if (options.stateful) ;
893
- }
894
- getDimensions() {
895
- const el = document.querySelector(".taskbar");
896
- if (!el) {
897
- return { width: 0, height: 0 };
898
- }
899
- return {
900
- width: el.clientWidth,
901
- height: el.clientHeight
902
- };
903
- }
904
- }
905
- globalThis.__defussTaskbarManager = globalThis.__defussTaskbarManager || new TaskbarManager();
906
- const taskbarManager = globalThis.__defussTaskbarManager;
907
-
908
1392
  const defaultSystemSoundFilePaths = [
909
1393
  "/sounds/balloon.ogg",
910
1394
  "/sounds/batterycritical.ogg",
@@ -1033,15 +1517,35 @@ class SoundManager {
1033
1517
  globalThis.__defussSoundManager = globalThis.__defussSoundManager || new SoundManager();
1034
1518
  const soundManager = globalThis.__defussSoundManager;
1035
1519
 
1520
+ const notepadAppBundle = {
1521
+ executable: "notepad.exe",
1522
+ displayName: "Notepad",
1523
+ icon: "/desktop/documents.png",
1524
+ width: 760,
1525
+ height: 520,
1526
+ load: () => Promise.resolve().then(function () { return require('./notepad-D2pcJRhu.cjs'); })
1527
+ };
1528
+
1529
+ const internetExplorerAppBundle = {
1530
+ executable: "explorer.exe",
1531
+ displayName: "Internet Explorer",
1532
+ icon: "/desktop/internet-explorer.png",
1533
+ width: 980,
1534
+ height: 680,
1535
+ load: () => Promise.resolve().then(function () { return require('./internet-explorer-CajUFG-6.cjs'); })
1536
+ };
1537
+
1036
1538
  exports.$ = $;
1037
1539
  exports.Button = Button;
1038
1540
  exports.DefussApp = DefussApp;
1039
1541
  exports.DefussDesktopAppIcon = DefussDesktopAppIcon;
1040
1542
  exports.DequeryWithWindowManager = DequeryWithWindowManager;
1041
1543
  exports.Desktop = Desktop;
1544
+ exports.DesktopIcon = DesktopIcon;
1042
1545
  exports.DesktopManager = DesktopManager;
1043
1546
  exports.DesktopShellManager = DesktopShellManager;
1044
1547
  exports.LogonScreen = LogonScreen;
1548
+ exports.SelectionModel = SelectionModel;
1045
1549
  exports.Shell = Shell;
1046
1550
  exports.SoundManager = SoundManager;
1047
1551
  exports.StartButton = StartButton;
@@ -1055,6 +1559,8 @@ exports.defaultTaskbarOptions = defaultTaskbarOptions;
1055
1559
  exports.defaultWindowOptions = defaultWindowOptions;
1056
1560
  exports.desktopManager = desktopManager;
1057
1561
  exports.desktopShell = desktopShell;
1562
+ exports.internetExplorerAppBundle = internetExplorerAppBundle;
1563
+ exports.notepadAppBundle = notepadAppBundle;
1058
1564
  exports.soundManager = soundManager;
1059
1565
  exports.taskbarManager = taskbarManager;
1060
1566
  exports.windowManager = windowManager;