@moonitoring/nidamjs 1.0.0 → 1.0.1

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/nidam.es.js CHANGED
@@ -1,5 +1,5 @@
1
- class x {
2
- static #t = /* @__PURE__ */ new WeakSet();
1
+ class I {
2
+ static #e = /* @__PURE__ */ new WeakSet();
3
3
  /**
4
4
  * Initialize feature modules in a container.
5
5
  *
@@ -8,28 +8,28 @@ class x {
8
8
  * @param {Map|null} modules - Shared module registry.
9
9
  * @param {Array<{selector:string, init:Function, name?:string}>} registry - Feature definitions.
10
10
  */
11
- static initialize(t, s = document, e = null, i = []) {
12
- !Array.isArray(i) || i.length === 0 || i.forEach(({ selector: o, init: n, name: a }) => {
13
- if (!o || typeof n != "function")
11
+ static initialize(t, e = document, s = null, i = []) {
12
+ !Array.isArray(i) || i.length === 0 || i.forEach(({ selector: o, init: r, name: n }) => {
13
+ if (!o || typeof r != "function")
14
14
  return;
15
- s.querySelectorAll(o).forEach((d) => {
16
- if (!this.#t.has(d))
15
+ e.querySelectorAll(o).forEach((d) => {
16
+ if (!this.#e.has(d))
17
17
  try {
18
- const c = n(d, t, e);
19
- this.#t.add(d), a && c && e && typeof e.set == "function" && e.set(a, c);
20
- } catch (c) {
21
- console.warn(`Failed to initialize ${o}:`, c);
18
+ const u = r(d, t, s);
19
+ this.#e.add(d), n && u && s && typeof s.set == "function" && s.set(n, u);
20
+ } catch (u) {
21
+ console.warn(`Failed to initialize ${o}:`, u);
22
22
  }
23
23
  });
24
24
  });
25
25
  }
26
26
  }
27
- class b {
28
- #t = /* @__PURE__ */ new Map();
29
- #s;
27
+ class k {
30
28
  #e = /* @__PURE__ */ new Map();
29
+ #t;
30
+ #s = /* @__PURE__ */ new Map();
31
31
  constructor(t = document) {
32
- this.#s = t, this.#i();
32
+ this.#t = t, this.#i();
33
33
  }
34
34
  #i() {
35
35
  [
@@ -42,21 +42,21 @@ class b {
42
42
  "mousedown",
43
43
  "desktop:toggle-matrix",
44
44
  "desktop:theme-changed"
45
- ].forEach((s) => {
46
- const e = (i) => this.#o(s, i);
47
- this.#s.addEventListener(s, e), this.#e.set(s, e);
45
+ ].forEach((e) => {
46
+ const s = (i) => this.#o(e, i);
47
+ this.#t.addEventListener(e, s), this.#s.set(e, s);
48
48
  });
49
49
  }
50
- #o(t, s) {
51
- const e = this.#t.get(t);
52
- if (!(!e || e.length === 0))
53
- for (const { selector: i, handler: o } of e) {
50
+ #o(t, e) {
51
+ const s = this.#e.get(t);
52
+ if (!(!s || s.length === 0))
53
+ for (const { selector: i, handler: o } of s) {
54
54
  if (!i) {
55
- o.call(this.#s, s, this.#s);
55
+ o.call(this.#t, e, this.#t);
56
56
  continue;
57
57
  }
58
- const n = s.target.closest(i);
59
- n && o.call(n, s, n);
58
+ const r = e.target.closest(i);
59
+ r && o.call(r, e, r);
60
60
  }
61
61
  }
62
62
  /**
@@ -66,34 +66,34 @@ class b {
66
66
  * @param {(e: Event, target: Element|Document) => void} handler
67
67
  * @param {{group?: string}} [options]
68
68
  */
69
- on(t, s, e, i = {}) {
69
+ on(t, e, s, i = {}) {
70
70
  const o = i.group;
71
- this.#t.has(t) || this.#t.set(t, []);
72
- const n = { selector: s, handler: e, group: o };
73
- return this.#t.get(t).push(n), n;
71
+ this.#e.has(t) || this.#e.set(t, []);
72
+ const r = { selector: e, handler: s, group: o };
73
+ return this.#e.get(t).push(r), r;
74
74
  }
75
- off(t, s) {
76
- const e = this.#t.get(t);
77
- if (!e) return;
78
- const i = e.indexOf(s);
79
- i !== -1 && e.splice(i, 1), e.length || this.#t.delete(t);
75
+ off(t, e) {
76
+ const s = this.#e.get(t);
77
+ if (!s) return;
78
+ const i = s.indexOf(e);
79
+ i !== -1 && s.splice(i, 1), s.length || this.#e.delete(t);
80
80
  }
81
81
  destroy() {
82
- for (const [t, s] of this.#e)
83
- this.#s.removeEventListener(t, s);
84
- this.#e.clear(), this.#t.clear();
82
+ for (const [t, e] of this.#s)
83
+ this.#t.removeEventListener(t, e);
84
+ this.#s.clear(), this.#e.clear();
85
85
  }
86
86
  }
87
- const R = {
87
+ const q = {
88
88
  /**
89
89
  * Move text cursor to the end of an editable element
90
90
  * @param {HTMLElement} element - Target element
91
91
  */
92
- moveCursorToEnd(r) {
92
+ moveCursorToEnd(a) {
93
93
  const t = document.createRange();
94
- t.selectNodeContents(r), t.collapse(!1);
95
- const s = window.getSelection();
96
- s.removeAllRanges(), s.addRange(t);
94
+ t.selectNodeContents(a), t.collapse(!1);
95
+ const e = window.getSelection();
96
+ e.removeAllRanges(), e.addRange(t);
97
97
  },
98
98
  /**
99
99
  * Register a callback for ESC key press
@@ -101,9 +101,9 @@ const R = {
101
101
  * @param {Function} callback - Function to call on ESC press
102
102
  * @returns {Function} Handler function for manual removal
103
103
  */
104
- onEscape(r) {
105
- const t = (s) => {
106
- s.key === "Escape" && (r(), document.removeEventListener("keydown", t));
104
+ onEscape(a) {
105
+ const t = (e) => {
106
+ e.key === "Escape" && (a(), document.removeEventListener("keydown", t));
107
107
  };
108
108
  return document.addEventListener("keydown", t), t;
109
109
  },
@@ -111,36 +111,36 @@ const R = {
111
111
  * Hide elements by adding 'hidden' class
112
112
  * @param {...HTMLElement} elements - Elements to hide
113
113
  */
114
- hide(...r) {
115
- r.forEach((t) => t?.classList.add("hidden"));
114
+ hide(...a) {
115
+ a.forEach((t) => t?.classList.add("hidden"));
116
116
  },
117
117
  /**
118
118
  * Show elements by removing 'hidden' class
119
119
  * @param {...HTMLElement} elements - Elements to show
120
120
  */
121
- show(...r) {
122
- r.forEach((t) => t?.classList.remove("hidden"));
121
+ show(...a) {
122
+ a.forEach((t) => t?.classList.remove("hidden"));
123
123
  },
124
124
  /**
125
125
  * Toggle visibility of elements
126
126
  * @param {...HTMLElement} elements - Elements to toggle
127
127
  */
128
- toggle(...r) {
129
- r.forEach((t) => t?.classList.toggle("hidden"));
128
+ toggle(...a) {
129
+ a.forEach((t) => t?.classList.toggle("hidden"));
130
130
  }
131
131
  };
132
- class z {
132
+ class R {
133
133
  _root;
134
134
  _elements = {};
135
135
  _state = {};
136
- _utils = R;
136
+ _utils = q;
137
137
  _delegator = null;
138
138
  /**
139
139
  * @param {HTMLElement|string} root - Root element or selector
140
140
  * @param {EventDelegator} delegator - Global event delegator instance
141
141
  */
142
- constructor(t, s) {
143
- if (this._root = typeof t == "string" ? document.querySelector(t) : t, this._delegator = s, !this._root)
142
+ constructor(t, e) {
143
+ if (this._root = typeof t == "string" ? document.querySelector(t) : t, this._delegator = e, !this._root)
144
144
  throw new Error(`Root element not found: ${t}`);
145
145
  this._initialize();
146
146
  }
@@ -178,8 +178,9 @@ class z {
178
178
  }
179
179
  /**
180
180
  * Query multiple elements within the root
181
- * @param {string} selector - CSS selector
182
- * @returns {NodeList}
181
+ * @template {HTMLElement} T
182
+ * @param {string} selector
183
+ * @returns {NodeListOf<T>}
183
184
  * @protected
184
185
  */
185
186
  _queryAll(t) {
@@ -192,708 +193,53 @@ class z {
192
193
  * @param {Function} handler
193
194
  * @protected
194
195
  */
195
- _on(t, s, e) {
196
- this._delegator.on(t, s, (i, o) => {
197
- this._root.contains(o) && e.call(this, i, o);
196
+ _on(t, e, s) {
197
+ this._delegator.on(t, e, (i, o) => {
198
+ this._root.contains(o) && s.call(this, i, o);
198
199
  });
199
200
  }
200
201
  }
201
- const W = /* @__PURE__ */ new WeakMap();
202
- function u(r) {
203
- return !r || r === "auto" || r === "normal" ? "" : r;
204
- }
205
- function w(r) {
206
- return Number.isFinite(r) ? `${Math.round(r)}px` : "";
202
+ function z(a) {
203
+ if (!a) return null;
204
+ const [t, e] = a.split(":").map((s) => parseInt(s.trim(), 10));
205
+ return Number.isFinite(t) && Number.isFinite(e) ? { col: t, row: e } : null;
207
206
  }
208
- function T(r, t = {}) {
209
- const s = t.includePosition === !0;
210
- let e = null;
211
- const i = () => (e || (e = window.getComputedStyle(r)), e), o = (r.offsetWidth > 0 ? w(r.offsetWidth) : "") || u(r.style.width) || u(i().width), n = (r.offsetHeight > 0 ? w(r.offsetHeight) : "") || u(r.style.height) || u(i().height);
212
- let a = "", l = "";
213
- return s && (a = w(r.offsetLeft) || u(r.style.left) || u(i().left), l = w(r.offsetTop) || u(r.style.top) || u(i().top)), {
214
- width: o || "",
215
- height: n || "",
216
- left: a,
217
- top: l
218
- };
219
- }
220
- function y(r) {
221
- let t = W.get(r);
222
- return t || (t = /* @__PURE__ */ new Map(), W.set(r, t)), t;
223
- }
224
- function C(r, t = "prevState", s = {}) {
225
- const e = T(r, s), i = JSON.stringify(e);
226
- return r.dataset[t] = i, y(r).set(t, { raw: i, parsed: e }), e;
227
- }
228
- function v(r, t = "prevState") {
229
- const s = r.dataset[t];
230
- if (!s) return null;
231
- const e = y(r).get(t);
232
- if (e && e.raw === s)
233
- return e.parsed;
234
- try {
235
- const i = JSON.parse(s);
236
- if (!i || typeof i != "object")
237
- return y(r).set(t, { raw: s, parsed: null }), null;
238
- const o = {
239
- width: u(i.width),
240
- height: u(i.height),
241
- left: u(i.left),
242
- top: u(i.top)
243
- };
244
- return y(r).set(t, { raw: s, parsed: o }), o;
245
- } catch {
246
- return y(r).set(t, { raw: s, parsed: null }), null;
247
- }
207
+ function F(a, t, e, s) {
208
+ const { cols: i, rows: o } = s, r = e.width / i, n = e.height / o, c = a - e.left, d = t - e.top;
209
+ let u = Math.floor(c / r) + 1, h = Math.floor(d / n) + 1;
210
+ return u = Math.max(1, Math.min(u, i)), h = Math.max(1, Math.min(h, o)), { col: u, row: h, posString: `${u}:${h}` };
248
211
  }
249
- function L(r, t, s = {}) {
250
- return !t || typeof t != "object" ? !1 : (t.width && r.style.width !== t.width && (r.style.width = t.width), t.height && r.style.height !== t.height && (r.style.height = t.height), s.includePosition && (t.left && r.style.left !== t.left && (r.style.left = t.left), t.top && r.style.top !== t.top && (r.style.top = t.top)), !0);
251
- }
252
- class D extends z {
253
- // Configuration
254
- _config = {
255
- zIndexBase: 40,
256
- layoutStabilizationMs: 450,
257
- cascadeOffset: 30,
258
- cooldownMs: 500,
259
- maxWindows: 10,
260
- snapGap: 6,
261
- taskbarHeight: 64,
262
- snapThreshold: 30,
263
- dragThreshold: 10,
264
- resizeDebounceMs: 6,
265
- animationDurationMs: 400,
266
- defaultWidth: 800,
267
- defaultHeight: 600,
268
- minMargin: 10,
269
- edgeDetectionRatio: 0.4,
270
- scrollRestoreTimeoutMs: 2e3
271
- };
272
- _windows = /* @__PURE__ */ new Map();
273
- _zIndexCounter = this._config.zIndexBase;
274
- _getModules = null;
275
- _notify = null;
276
- _fetchWindowContent = null;
277
- _initializeContent = null;
278
- _resolveEndpoint = null;
279
- _lastOpenTimestamps = /* @__PURE__ */ new Map();
280
- _pendingRequests = /* @__PURE__ */ new Map();
281
- // Tiling & Snapping properties
282
- _snapIndicator = null;
283
- constructor(t, s, e = {}) {
284
- super(t, s);
285
- const {
286
- getModules: i = null,
287
- config: o = null,
288
- notify: n = null,
289
- fetchWindowContent: a = null,
290
- initializeContent: l = null,
291
- resolveEndpoint: d = null
292
- } = e || {};
293
- this._getModules = i, this._notify = n || this._defaultNotify.bind(this), this._fetchWindowContent = a || this._defaultFetchWindowContent.bind(this), this._initializeContent = l || (() => {
294
- }), this._resolveEndpoint = d || this._defaultResolveEndpoint, o && typeof o == "object" && (this._config = { ...this._config, ...o }), this._zIndexCounter = this._config.zIndexBase, this._initSnapIndicator();
295
- }
296
- // Create the visual indicator for window snapping
297
- _initSnapIndicator() {
298
- this._snapIndicator = document.createElement("div"), this._snapIndicator.className = "snap-indicator", document.body.appendChild(this._snapIndicator);
299
- }
300
- _cacheElements() {
301
- return {};
302
- }
303
- _bindEvents() {
304
- this._delegator.on(
305
- "click",
306
- "[data-modal]",
307
- this._handleModalTrigger.bind(this)
308
- ), this._delegator.on(
309
- "click",
310
- "[data-maximize]",
311
- this._handleMaximizeTrigger.bind(this)
312
- ), this._delegator.on(
313
- "click",
314
- "[data-close]",
315
- this._handleCloseTrigger.bind(this)
316
- ), this._delegator.on(
317
- "mousedown",
318
- ".window",
319
- this._handleWindowFocus.bind(this)
320
- ), this._delegator.on(
321
- "mousedown",
322
- "[data-bar]",
323
- this._handleWindowDragStart.bind(this)
324
- ), document.addEventListener("keydown", this._handleGlobalKeydown.bind(this));
325
- let t;
326
- window.addEventListener("resize", () => {
327
- clearTimeout(t), t = setTimeout(
328
- () => this._handleResize(),
329
- this._config.resizeDebounceMs
330
- );
331
- });
332
- }
333
- // Event Handlers
334
- _handleModalTrigger(t, s) {
335
- t.preventDefault(), this.open(s.dataset.modal).catch((e) => {
336
- console.debug("Modal trigger failed:", e);
337
- });
338
- }
339
- _handleCloseTrigger(t, s) {
340
- t.preventDefault();
341
- const e = s.closest(".window");
342
- e && this.close(e);
343
- }
344
- _handleWindowFocus(t, s) {
345
- if (t.target.closest("[data-close]") || t.target.closest("[data-modal]"))
346
- return;
347
- const e = s.closest(".window");
348
- e && this._focusWindow(e);
349
- }
350
- _handleMaximizeTrigger(t, s) {
351
- t.preventDefault();
352
- const e = s.closest(".window");
353
- e && this.toggleMaximize(e);
354
- }
355
- _handleWindowDragStart(t, s) {
356
- if (t.target.closest("[data-close]") || t.target.closest("[data-maximize]"))
357
- return;
358
- t.preventDefault();
359
- const e = s.closest(".window");
360
- e && (this._focusWindow(e), this.drag(t, e));
361
- }
362
- _handleGlobalKeydown(t) {
363
- t.key === "Escape" && !t.repeat && this._closeTopmostWindow();
364
- }
365
- // Public Methods
366
- // Toggle between maximized and normal state
367
- toggleMaximize(t) {
368
- const s = t.classList.contains("maximized"), e = t.classList.contains("tiled") && typeof t.dataset.snapType == "string" && t.dataset.snapType.length > 0;
369
- t.classList.add("window-toggling"), !s && !t.classList.contains("tiled") && this._ensureRestoreState(t);
370
- const i = t.classList.toggle("maximized");
371
- let o = !1;
372
- if (this._updateMaximizeIcon(t, i), !i)
373
- if (e) {
374
- const n = this._getSnapLayout(
375
- t.dataset.snapType,
376
- window.innerWidth,
377
- window.innerHeight - this._config.taskbarHeight
378
- );
379
- Object.assign(t.style, n);
380
- } else {
381
- const n = v(t);
382
- L(t, n);
383
- const a = this._parseCssPixelValue(n?.width) || this._parseCssPixelValue(t.style.width) || t.offsetWidth, l = this._parseCssPixelValue(n?.height) || this._parseCssPixelValue(t.style.height) || t.offsetHeight;
384
- this._repositionWindowFromRatios(
385
- t,
386
- window.innerWidth,
387
- window.innerHeight,
388
- {
389
- widthPx: a,
390
- heightPx: l
391
- }
392
- ), o = !0;
393
- }
394
- setTimeout(() => {
395
- t.classList.remove("window-toggling"), o && this._savePositionRatios(t);
396
- }, this._config.animationDurationMs);
397
- }
398
- // Open a window by its endpoint
399
- async open(t, s = !1, e = null, i = !0) {
400
- if (this._windows.size >= this._config.maxWindows && !this._windows.has(t)) {
401
- const a = document.body.dataset.errorMaxWindows || `Maximum of ${this._config.maxWindows} windows allowed.`;
402
- return this._notify("error", a.replace("%s", String(this._config.maxWindows))), Promise.reject(new Error("Max windows reached"));
403
- }
404
- if (this._windows.has(t) && !s) {
405
- const a = this._windows.get(t);
406
- return i && this._focusWindow(a), Promise.resolve(a);
407
- }
408
- if (this._pendingRequests.has(t))
409
- return this._pendingRequests.get(t);
410
- const o = Date.now();
411
- if (!s && o - (this._lastOpenTimestamps.get(t) || 0) < this._config.cooldownMs)
412
- return Promise.resolve();
413
- this._lastOpenTimestamps.set(t, o);
414
- const n = (async () => {
415
- try {
416
- const a = await this._fetchWindowContent(t, {
417
- force: s,
418
- focusSelector: e,
419
- activate: i,
420
- manager: this
421
- });
422
- if (typeof a != "string")
423
- throw new TypeError("fetchWindowContent must return an HTML string");
424
- if (this._windows.has(t) && s) {
425
- const d = this._windows.get(t);
426
- return !i && this._isWindowBusy(d) || (this._refreshWindowContent(d, a), i && this._focusWindow(d), e && this._handleFocusSelector(d, e)), d;
427
- }
428
- const l = this._createWindowElement(a, t);
429
- if (!l) {
430
- console.warn(`No .window element found for ${t}`);
431
- return;
432
- }
433
- return this._setupNewWindow(l, t, e, i), l;
434
- } catch (a) {
435
- console.error("Error opening window:", a);
436
- const l = document.body.dataset.errorOpenFailed || "Failed to open window.";
437
- throw this._notify("error", l), a;
438
- } finally {
439
- this._pendingRequests.delete(t);
440
- }
441
- })();
442
- return this._pendingRequests.set(t, n), n;
443
- }
444
- // Close a window
445
- close(t) {
446
- const s = t.dataset.endpoint;
447
- this._windows.get(s) === t && this._windows.delete(s), t.classList.add("animate-disappearance"), t.classList.remove("animate-appearance"), t.addEventListener(
448
- "animationend",
449
- () => {
450
- t.isConnected && t.remove();
451
- },
452
- { once: !0 }
453
- );
454
- }
455
- // Internal Logic
456
- // Focus a window and bring it to top
457
- _focusWindow(t) {
458
- this._zIndexCounter++, t.style.zIndex = this._zIndexCounter, t.classList.add("focused"), this._windows.forEach((s) => {
459
- s !== t && s.classList.remove("focused");
460
- });
461
- }
462
- _isWindowBusy(t) {
463
- return t.dataset.isBusy === "true" ? !0 : t.querySelector('[data-is-busy="true"]') !== null;
464
- }
465
- _refreshWindowContent(t, s) {
466
- const o = (
467
- /** @type {HTMLElement|null} */
468
- new DOMParser().parseFromString(s, "text/html").querySelector(".window")
469
- );
470
- if (!o) return;
471
- const n = t.dataset.snapType, a = t.dataset.prevState, l = t.dataset.xRatio, d = t.dataset.yRatio, c = t.classList.contains("focused"), h = t.classList.contains("maximized"), f = t.classList.contains("tiled"), g = this._captureScrollState(t);
472
- t.innerHTML = o.innerHTML, t.className = o.className, n && (t.dataset.snapType = n), a && (t.dataset.prevState = a), l && (t.dataset.xRatio = l), d && (t.dataset.yRatio = d), c && t.classList.add("focused"), f && t.classList.add("tiled"), h && (t.classList.add("maximized"), this._updateMaximizeIcon(t, !0)), !f && !h && (o.style.width && (t.style.width = o.style.width), o.style.height && (t.style.height = o.style.height)), t.style.margin = "0", t.style.transform = "none", this._restoreScrollState(t, g), this._initializeModalContent(t);
473
- }
474
- _updateMaximizeIcon(t, s) {
475
- const e = t.querySelector("[data-maximize] i");
476
- e && (e.classList.toggle("fa-expand", !s), e.classList.toggle("fa-compress", s));
477
- }
478
- _createWindowElement(t, s) {
479
- const o = (
480
- /** @type {HTMLElement|null} */
481
- new DOMParser().parseFromString(t, "text/html").querySelector(".window")
482
- );
483
- return o && (o.dataset.endpoint = s), o;
484
- }
485
- _setupNewWindow(t, s, e, i = !0) {
486
- Object.assign(t.style, {
487
- position: "absolute",
488
- pointerEvents: "auto",
489
- margin: "0",
490
- transform: "none",
491
- visibility: "hidden"
492
- }), this._root.appendChild(t);
493
- const o = this._windows.size, n = t.dataset.defaultSnap;
494
- if (n) {
495
- const a = window.innerWidth, l = window.innerHeight - this._config.taskbarHeight;
496
- this._snapWindow(t, n, a, l);
497
- } else
498
- this._positionWindow(t, o), this._ensureRestoreState(t);
499
- this._windows.set(s, t), this._initializeModalContent(t), i && this._focusWindow(t), t.style.visibility = "", n || this._stabilizeInitialPlacement(t, o), e && this._handleFocusSelector(t, e);
500
- }
501
- _positionWindow(t, s = null) {
502
- const e = t.offsetWidth || parseInt(t.style.width) || this._config.defaultWidth, i = t.offsetHeight || parseInt(t.style.height) || this._config.defaultHeight, o = window.innerWidth, n = window.innerHeight, a = Number.isFinite(s) && s >= 0 ? s : this._windows.size, l = a * this._config.cascadeOffset, d = a * this._config.cascadeOffset;
503
- let c = (o - e) / 2 + l, h = (n - i) / 2 + d;
504
- const f = this._config.minMargin;
505
- c + e > o && (c = Math.max(f, o - e - f)), h + i > n && (h = Math.max(f, n - i - f)), t.style.left = `${Math.round(c)}px`, t.style.top = `${Math.round(h)}px`, this._savePositionRatios(t);
506
- }
507
- _stabilizeInitialPlacement(t, s) {
508
- if (!t?.isConnected) return;
509
- const e = Number.isFinite(this._config.layoutStabilizationMs) && this._config.layoutStabilizationMs > 0 ? this._config.layoutStabilizationMs : 450, i = typeof performance < "u" ? () => performance.now() : () => Date.now(), o = i();
510
- let n = !0, a = null, l = t.offsetWidth, d = t.offsetHeight;
511
- const c = () => {
512
- n && (n = !1, a && (a.disconnect(), a = null));
513
- }, h = () => {
514
- if (!n || !t.isConnected) {
515
- c();
516
- return;
517
- }
518
- if (t.classList.contains("tiled") || t.classList.contains("maximized"))
519
- return;
520
- const g = t.offsetWidth, _ = t.offsetHeight;
521
- (g !== l || _ !== d) && (l = g, d = _, this._positionWindow(t, s));
522
- }, f = () => {
523
- if (n) {
524
- if (h(), i() - o < e) {
525
- requestAnimationFrame(f);
526
- return;
527
- }
528
- c();
529
- }
530
- };
531
- requestAnimationFrame(f), typeof ResizeObserver == "function" && (a = new ResizeObserver(() => h()), a.observe(t)), setTimeout(() => c(), e);
532
- }
533
- // Save relative position ratios (Center-based)
534
- _savePositionRatios(t) {
535
- if (t.classList.contains("tiled") || t.classList.contains("maximized"))
536
- return;
537
- const s = t.offsetLeft + t.offsetWidth / 2, e = t.offsetTop + t.offsetHeight / 2;
538
- t.dataset.xRatio = String(s / window.innerWidth), t.dataset.yRatio = String(e / window.innerHeight);
539
- }
540
- _ensureRestoreState(t) {
541
- const s = v(t);
542
- return s?.width && s?.height ? s : C(t, "prevState", { includePosition: !1 });
543
- }
544
- _parseCssPixelValue(t) {
545
- if (!t) return null;
546
- const s = parseFloat(t);
547
- return Number.isFinite(s) ? s : null;
548
- }
549
- _repositionWindowFromRatios(t, s, e, i = null) {
550
- const o = parseFloat(t.dataset.xRatio), n = parseFloat(t.dataset.yRatio);
551
- if (isNaN(o) || isNaN(n)) return !1;
552
- const a = (i && Number.isFinite(i.widthPx) && i.widthPx > 0 ? i.widthPx : null) || t.offsetWidth, l = (i && Number.isFinite(i.heightPx) && i.heightPx > 0 ? i.heightPx : null) || t.offsetHeight, d = o * s, c = n * e;
553
- return t.style.left = `${Math.round(d - a / 2)}px`, t.style.top = `${Math.round(c - l / 2)}px`, !0;
554
- }
555
- _handleFocusSelector(t, s) {
556
- const e = t.querySelector(s);
557
- e && ((e.type === "radio" || e.type === "checkbox") && (e.checked = !0), e.focus());
558
- }
559
- _closeTopmostWindow() {
560
- let t = null, s = 0;
561
- this._windows.forEach((e) => {
562
- if (e.classList.contains("animate-disappearance")) return;
563
- const i = parseInt(e.style.zIndex || 0, 10);
564
- i > s && (s = i, t = e);
565
- }), t && this.close(t);
566
- }
567
- // Drag & Tiling Methods
568
- drag(t, s) {
569
- this._dragState?.active || (this._dragState = {
570
- active: !0,
571
- winElement: s,
572
- startX: t.clientX,
573
- startY: t.clientY,
574
- currentX: t.clientX,
575
- currentY: t.clientY,
576
- startWinLeft: s.offsetLeft,
577
- startWinTop: s.offsetTop,
578
- isRestored: !1,
579
- restoreXRatio: null,
580
- initialState: {
581
- tiled: s.classList.contains("tiled"),
582
- maximized: s.classList.contains("maximized")
583
- },
584
- view: {
585
- w: window.innerWidth,
586
- h: window.innerHeight - this._config.taskbarHeight
587
- },
588
- snap: null,
589
- inhibitSnap: !1,
590
- isDragging: !1
591
- }, this._dragHandlers = {
592
- move: (e) => {
593
- this._dragState.currentX = e.clientX, this._dragState.currentY = e.clientY;
594
- },
595
- stop: () => this._handleDragStop()
596
- }, document.addEventListener("mousemove", this._dragHandlers.move, {
597
- passive: !0
598
- }), document.addEventListener("mouseup", this._dragHandlers.stop), requestAnimationFrame(() => this._dragLoop()));
599
- }
600
- _dragLoop() {
601
- this._dragState?.active && (this._updateDragPosition(), requestAnimationFrame(() => this._dragLoop()));
602
- }
603
- _updateDragPosition() {
604
- const t = this._dragState, { winElement: s, currentX: e, currentY: i, startX: o, startY: n } = t, a = e - o, l = i - n;
605
- if (!t.isDragging && (Math.abs(a) > this._config.dragThreshold || Math.abs(l) > this._config.dragThreshold) && (t.isDragging = !0), !t.isDragging) return;
606
- (t.initialState.tiled || t.initialState.maximized) && !t.isRestored && t.isDragging && (t.initialState.maximized ? (t.restoreXRatio = o / window.innerWidth, this._restoreWindowInternal(s, t.restoreXRatio), t.startWinTop = 0) : (t.restoreXRatio = (o - s.offsetLeft) / s.offsetWidth, this._restoreWindowInternal(s, null), t.startWinTop = s.offsetTop), t.startX = e, t.startY = i, t.isRestored = !0);
607
- let d, c;
608
- if (t.isRestored && t.restoreXRatio !== null) {
609
- const h = s.offsetWidth;
610
- d = e - t.restoreXRatio * h, c = Math.max(0, t.startWinTop + (i - n));
611
- } else
612
- d = t.startWinLeft + (t.isRestored ? e - t.startX : a), c = Math.max(
613
- 0,
614
- t.startWinTop + (t.isRestored ? i - t.startY : l)
615
- );
616
- s.style.left = `${d}px`, s.style.top = `${c}px`, (t.isRestored || !t.initialState.tiled && !t.initialState.maximized) && (s.classList.contains("tiled") && s.classList.remove("tiled"), s.classList.contains("maximized") && (s.classList.remove("maximized"), this._updateMaximizeIcon(s, !1))), t.isDragging && this._detectSnapZone(e, i);
617
- }
618
- _detectSnapZone(t, s) {
619
- const { view: e } = this._dragState, i = this._config.snapThreshold, o = e.w * this._config.edgeDetectionRatio, n = e.h * this._config.edgeDetectionRatio;
620
- let a = null;
621
- s < i ? t < o ? a = "tl" : t > e.w - o ? a = "tr" : a = "maximize" : t < i ? s < n ? a = "tl" : s > e.h - n ? a = "bl" : a = "left" : t > e.w - i ? s < n ? a = "tr" : s > e.h - n ? a = "br" : a = "right" : s > e.h - i && (a = t < e.w / 2 ? "bl" : "br"), this._dragState.snap !== a && (this._dragState.snap = a, this._updateSnapIndicator(a, e.w, e.h));
622
- }
623
- _handleDragStop() {
624
- if (!this._dragState?.active) return;
625
- const { winElement: t, snap: s, view: e } = this._dragState;
626
- document.removeEventListener("mousemove", this._dragHandlers.move), document.removeEventListener("mouseup", this._dragHandlers.stop), this._snapIndicator.classList.remove("visible"), s ? s === "maximize" ? this.toggleMaximize(t) : this._snapWindow(t, s, e.w, e.h) : this._savePositionRatios(t), this._dragState.active = !1, this._dragState = null, this._dragHandlers = null;
627
- }
628
- // Unified Restore Logic
629
- _restoreWindowInternal(t, s) {
630
- let e, i;
631
- const o = v(t);
632
- s === null ? o && (e = o.width, i = o.height) : (e = o?.width || t.style.width, i = o?.height || t.style.height), (!e || e === "100%") && (e = this._config.defaultWidth + "px"), (!i || i === "100%") && (i = this._config.defaultHeight + "px"), t.classList.remove("maximized", "tiled"), this._updateMaximizeIcon(t, !1), t.classList.add("window-toggling", "dragging-restore"), L(t, { width: e, height: i }), setTimeout(() => {
633
- t.classList.remove("window-toggling", "dragging-restore"), this._savePositionRatios(t);
634
- }, this._config.animationDurationMs);
635
- }
636
- // Update snap indicator visibility and position
637
- _updateSnapIndicator(t, s, e) {
638
- if (!t) {
639
- this._snapIndicator.classList.remove("visible");
640
- return;
641
- }
642
- let i;
643
- t === "maximize" ? i = {
644
- top: "0px",
645
- left: "0px",
646
- width: `${s}px`,
647
- height: `${e}px`
648
- } : i = this._getSnapLayout(t, s, e), Object.assign(this._snapIndicator.style, i), this._snapIndicator.classList.add("visible");
649
- }
650
- // Snap window to a specific quadrant
651
- _snapWindow(t, s, e, i) {
652
- t.classList.contains("tiled") || this._ensureRestoreState(t), t.classList.add("window-toggling", "tiled"), t.dataset.snapType = s;
653
- const o = this._getSnapLayout(s, e, i);
654
- Object.assign(t.style, o), setTimeout(
655
- () => t.classList.remove("window-toggling"),
656
- this._config.animationDurationMs
657
- );
658
- }
659
- // Calculate dimensions and position for a snap quadrant
660
- _getSnapLayout(t, s, e) {
661
- const i = this._config.snapGap, o = (s - i * 3) / 2, n = (e - i * 3) / 2, a = e - i * 2, l = i, d = o + i * 2, c = i, h = n + i * 2, g = {
662
- // Quadrants
663
- tl: { top: c, left: l, width: o, height: n },
664
- tr: { top: c, left: d, width: o, height: n },
665
- bl: { top: h, left: l, width: o, height: n },
666
- br: { top: h, left: d, width: o, height: n },
667
- // Vertical Splits
668
- left: { top: c, left: l, width: o, height: a },
669
- right: { top: c, left: d, width: o, height: a }
670
- }[t];
671
- return {
672
- width: `${g.width}px`,
673
- height: `${g.height}px`,
674
- top: `${g.top}px`,
675
- left: `${g.left}px`
676
- };
677
- }
678
- // Handle browser window resize to keep tiled windows in place
679
- _handleResize() {
680
- const t = window.innerWidth, s = window.innerHeight, e = s - this._config.taskbarHeight;
681
- this._windows.forEach((i) => {
682
- if (i.classList.contains("tiled") && i.dataset.snapType) {
683
- const o = i.dataset.snapType, n = this._getSnapLayout(o, t, e);
684
- Object.assign(i.style, n);
685
- } else i.classList.contains("maximized") || this._repositionWindowFromRatios(i, t, s);
686
- });
687
- }
688
- // Capture scroll positions of an element and its descendants
689
- _captureScrollState(t) {
690
- const s = /* @__PURE__ */ new Map();
691
- return (t.scrollTop > 0 || t.scrollLeft > 0) && s.set("root", {
692
- top: t.scrollTop,
693
- left: t.scrollLeft
694
- }), t.querySelectorAll("*").forEach((e) => {
695
- (e.scrollTop > 0 || e.scrollLeft > 0) && s.set(this._getElementPath(t, e), {
696
- top: e.scrollTop,
697
- left: e.scrollLeft
698
- });
699
- }), s;
700
- }
701
- // Restore scroll positions and observe layout shifts for stabilization
702
- _restoreScrollState(t, s) {
703
- const e = () => {
704
- s.forEach((n, a) => {
705
- let l;
706
- if (a === "root")
707
- l = t;
708
- else
709
- try {
710
- l = t.querySelector(`:scope > ${a}`);
711
- } catch {
712
- l = t.querySelector(a);
713
- }
714
- l && (l.scrollTop = n.top, l.scrollLeft = n.left);
715
- });
716
- };
717
- e();
718
- const i = t.querySelector(".window-content-scrollable > div") || t, o = new ResizeObserver(() => e());
719
- o.observe(i), setTimeout(
720
- () => o.disconnect(),
721
- this._config.scrollRestoreTimeoutMs
722
- );
723
- }
724
- // Generate unique CSS path for an element relative to window
725
- _getElementPath(t, s) {
726
- let e = [], i = s;
727
- for (; i && i !== t; ) {
728
- let o = Array.prototype.indexOf.call(
729
- i.parentNode.children,
730
- i
731
- );
732
- e.unshift(`${i.tagName}:nth-child(${o + 1})`), i = i.parentNode;
733
- }
734
- return e.join(" > ");
735
- }
736
- async _defaultFetchWindowContent(t) {
737
- return (await fetch(this._resolveEndpoint(t), {
738
- headers: { "X-Modal-Request": "1" },
739
- cache: "no-cache"
740
- })).text();
741
- }
742
- _defaultResolveEndpoint(t) {
743
- return `/${String(t || "").replace(/^\/+/, "")}`;
744
- }
745
- _defaultNotify(t, s) {
746
- (t === "error" ? console.error : console.log)(`[nidamjs:${t}]`, s);
747
- }
748
- _initializeModalContent(t) {
749
- const s = this._getModules ? this._getModules() : null;
750
- this._initializeContent(t, {
751
- delegator: this._delegator,
752
- modules: s,
753
- manager: this
754
- });
755
- }
212
+ function C(a, t, e) {
213
+ return Array.from(a).some(
214
+ (s) => s !== t && s.getAttribute("nd-icon") === e
215
+ );
756
216
  }
757
- class I {
758
- #t;
759
- #s;
760
- #e = 200;
761
- constructor(t, { refreshMap: s = null, refreshTimeout: e = 200 } = {}) {
762
- this.#t = t, this.#s = s || window.window_refresh_map || {}, this.#e = e;
763
- }
764
- setRefreshMap(t = {}) {
765
- this.#s = t || {};
766
- }
767
- handleEvent(t, s) {
768
- const e = this.#s[t], [i, o] = t.split(":"), n = o === "deleted", a = s?.id || null;
769
- this.#t && Array.from(this.#t._windows.entries()).forEach(
770
- ([l, d]) => {
771
- const c = l.startsWith("/") ? l.slice(1) : l;
772
- if (n && a && d.dataset.dependsOn && d.dataset.dependsOn.split("|").some((g) => {
773
- const [_, m] = g.split(":");
774
- return i === _ && String(m) === String(a);
775
- })) {
776
- setTimeout(() => {
777
- this.#t.close(d);
778
- }, this.#e);
779
- return;
217
+ const X = (a, t, e, s) => {
218
+ const { col: i, row: o } = z(t), r = Math.max(e, s);
219
+ for (let n = 0; n <= r; n++)
220
+ for (let c = -n; c <= n; c++)
221
+ for (let d = -n; d <= n; d++) {
222
+ if (Math.max(Math.abs(c), Math.abs(d)) !== n) continue;
223
+ const u = i + c, h = o + d;
224
+ if (u >= 1 && u <= e && h >= 1 && h <= s) {
225
+ const p = `${u}:${h}`;
226
+ if (!C(a, null, p))
227
+ return p;
780
228
  }
781
- e && e.forEach((h) => {
782
- this.#i(
783
- h,
784
- c,
785
- n ? null : a
786
- ) && setTimeout(() => {
787
- this.#t.open(l, !0, null, !1);
788
- }, this.#e);
789
- });
790
229
  }
791
- );
792
- }
793
- /**
794
- * Matches a route pattern against an actual path with strict validation.
795
- *
796
- * Rules:
797
- * 1. Exact Match: "team" matches "team", but NOT "team/5" (length mismatch).
798
- * 2. Wildcard (*): "team/*" matches "team/5", "team/12/details", etc.
799
- * 3. Parameters ({param}): "team/{id}" matches "team/5".
800
- * - If entityId is provided, it MUST match the parameter value (e.g. entityId "5" matches "team/5", but not "team/12").
801
- * - Parameter syntax must be enclosed in braces: {id}.
802
- *
803
- * @param {string} pattern - The route pattern from configuration.
804
- * @param {string} path - The actual window endpoint path.
805
- * @param {string|null} entityId - Optional ID to enforce specific parameter matching.
806
- */
807
- #i(t, s, e = null) {
808
- if (t === s) return !0;
809
- const i = t.split("/"), o = s.split("/");
810
- return !(i[i.length - 1] === "*") && i.length !== o.length ? !1 : i.every((a, l) => a === "*" ? !0 : a.startsWith("{") && a.endsWith("}") ? e !== null ? String(o[l]) === String(e) : !0 : a === o[l]);
811
- }
812
- }
813
- const H = (r, t) => {
814
- (r === "error" ? console.error : console.log)(`[nidamjs:${r}]`, t);
815
- };
816
- class P {
817
- #t;
818
- #s = /* @__PURE__ */ new Map();
819
- #e = null;
820
- constructor(t = {}) {
821
- this.#t = {
822
- root: document,
823
- modalContainer: "#target",
824
- pendingModalDatasetKey: "pendingModal",
825
- registry: [],
826
- refreshMap: null,
827
- refreshTimeout: 200,
828
- notify: H,
829
- windowManager: {},
830
- ...t
831
- };
832
- }
833
- initialize() {
834
- return this.#i(), this.#o(), this.#r(), this;
835
- }
836
- getModule(t) {
837
- return this.#s.get(t);
838
- }
839
- getModules() {
840
- return this.#s;
841
- }
842
- #i() {
843
- this.#e = new b(this.#t.root), this.#s.set("delegator", this.#e);
844
- }
845
- #o() {
846
- const t = this.#t.root.querySelector(
847
- this.#t.modalContainer
848
- );
849
- if (!t)
850
- return;
851
- const s = new D(t, this.#e, {
852
- getModules: () => this.#s,
853
- initializeContent: (i, o) => x.initialize(
854
- o.delegator,
855
- i,
856
- o.modules,
857
- this.#t.registry
858
- ),
859
- notify: this.#t.notify,
860
- ...this.#t.windowManager || {}
861
- });
862
- this.#s.set("window", s), this.#a(t, s);
863
- const e = new I(s, {
864
- refreshMap: this.#t.refreshMap,
865
- refreshTimeout: this.#t.refreshTimeout
866
- });
867
- this.#s.set("refresher", e);
868
- }
869
- #a(t, s) {
870
- const e = this.#t.pendingModalDatasetKey, i = (t?.dataset?.[e] || "").trim();
871
- if (!i)
872
- return;
873
- const o = i.startsWith("/") ? i.slice(1) : i;
874
- s.open(o);
875
- }
876
- #r() {
877
- x.initialize(
878
- this.#e,
879
- this.#t.root,
880
- this.#s,
881
- this.#t.registry
882
- );
883
- }
884
- }
885
- const $ = (r = {}) => new P(r), M = {
230
+ return t;
231
+ }, O = {
886
232
  /**
887
233
  * Saves a value to localStorage. Automatically stringifies objects/arrays.
888
234
  * @param {string} key
889
235
  * @param {*} value
890
236
  */
891
- set(r, t) {
237
+ set(a, t) {
892
238
  try {
893
- const s = JSON.stringify(t);
894
- localStorage.setItem(r, s);
895
- } catch (s) {
896
- console.error(`Error saving to localStorage (key: ${r}):`, s);
239
+ const e = JSON.stringify(t);
240
+ localStorage.setItem(a, e);
241
+ } catch (e) {
242
+ console.error(`Error saving to localStorage (key: ${a}):`, e);
897
243
  }
898
244
  },
899
245
  /**
@@ -902,23 +248,23 @@ const $ = (r = {}) => new P(r), M = {
902
248
  * @param {*} defaultValue - Returned if the key doesn't exist or on error.
903
249
  * @returns {*}
904
250
  */
905
- get(r, t = null) {
251
+ get(a, t = null) {
906
252
  try {
907
- const s = localStorage.getItem(r);
908
- return s === null ? t : JSON.parse(s);
909
- } catch (s) {
910
- return console.error(`Error reading from localStorage (key: ${r}):`, s), t;
253
+ const e = localStorage.getItem(a);
254
+ return e === null ? t : JSON.parse(e);
255
+ } catch (e) {
256
+ return console.error(`Error reading from localStorage (key: ${a}):`, e), t;
911
257
  }
912
258
  },
913
259
  /**
914
260
  * Removes a specific item from localStorage.
915
261
  * @param {string} key
916
262
  */
917
- remove(r) {
263
+ remove(a) {
918
264
  try {
919
- localStorage.removeItem(r);
265
+ localStorage.removeItem(a);
920
266
  } catch (t) {
921
- console.error(`Error removing from localStorage (key: ${r}):`, t);
267
+ console.error(`Error removing from localStorage (key: ${a}):`, t);
922
268
  }
923
269
  },
924
270
  /**
@@ -927,8 +273,8 @@ const $ = (r = {}) => new P(r), M = {
927
273
  clear() {
928
274
  try {
929
275
  localStorage.clear();
930
- } catch (r) {
931
- console.error("Error clearing localStorage:", r);
276
+ } catch (a) {
277
+ console.error("Error clearing localStorage:", a);
932
278
  }
933
279
  },
934
280
  /**
@@ -936,161 +282,1347 @@ const $ = (r = {}) => new P(r), M = {
936
282
  * @param {string} key
937
283
  * @returns {boolean}
938
284
  */
939
- has(r) {
285
+ has(a) {
940
286
  try {
941
- return localStorage.getItem(r) !== null;
287
+ return localStorage.getItem(a) !== null;
942
288
  } catch {
943
289
  return !1;
944
290
  }
945
291
  }
946
292
  };
947
- class X extends z {
948
- _dragState = null;
949
- _storageKey = "desktop_grid_layout";
950
- _storageNamespace = "";
951
- _storage = M;
952
- constructor(t, s, e = {}) {
953
- super(t, s), this._storageKey = e.storageKey || this._storageKey, this._storageNamespace = e.storageNamespace || "", this._storage = e.storage || M, this._loadLayout();
954
- }
955
- _getStorageKey() {
956
- return this._storageNamespace ? `${this._storageNamespace}_${this._storageKey}` : this._storageKey;
957
- }
958
- _loadLayout() {
959
- try {
960
- const t = this._getStorageKey(), s = this._storage.get(t, []);
961
- if (!s || !Array.isArray(s)) return;
962
- /** @type {NodeListOf<HTMLElement>} */
963
- this._root.querySelectorAll(".desktop-icon").forEach((i) => {
964
- const o = i.dataset.modal, n = s.find((a) => a.id === o);
965
- if (o && n && Array.isArray(n.classes)) {
966
- const a = Array.from(i.classList).filter(
967
- (l) => l.startsWith("col-start-") || l.startsWith("row-start-") || l.startsWith("col-end-")
968
- );
969
- i.classList.remove(...a), i.classList.add(...n.classes);
970
- }
971
- });
972
- } catch (t) {
973
- console.error("Failed to load desktop layout", t);
974
- }
293
+ class V extends R {
294
+ _key = "nd-icons-layout";
295
+ constructor(t, e) {
296
+ super(t, e), this.#e();
297
+ }
298
+ #e() {
299
+ this.#t(), this.#s(), this.#i();
975
300
  }
976
301
  _bindEvents() {
977
- this._delegator.on(
978
- "mousedown",
979
- ".desktop-icon",
980
- this._handleDragStart.bind(this)
302
+ this._on("mousedown", "[nd-icon]", this._handleStartDrag.bind(this));
303
+ }
304
+ #t() {
305
+ const t = this._root.getAttribute("nd-icons");
306
+ if (!t) return;
307
+ const [e, s] = t.split(":").map(Number), i = (
308
+ /** @type {HTMLElement} */
309
+ this._root
981
310
  );
311
+ e && s && (i.style.setProperty("--nd-cols", String(e)), i.style.setProperty("--nd-rows", String(s)));
312
+ }
313
+ #s() {
314
+ const t = O.get(this._key, {});
315
+ this._queryAll("[nd-icon]").forEach((e) => {
316
+ const s = e.getAttribute("nd-id");
317
+ if (!s) return;
318
+ const i = t[s];
319
+ i && e.setAttribute("nd-icon", i);
320
+ });
982
321
  }
983
- _handleDragStart(t, s) {
984
- if (t.button !== 0) return;
985
- t.preventDefault();
986
- const e = s.closest(".desktop-icon"), i = e.getBoundingClientRect(), o = this._root.getBoundingClientRect(), n = Array.from(e.classList).filter(
987
- (a) => a.startsWith("col-start-") || a.startsWith("row-start-") || a.startsWith("col-end-")
988
- );
989
- this._dragState = {
990
- element: e,
991
- startX: t.clientX,
992
- startY: t.clientY,
993
- initialLeft: i.left - o.left,
994
- initialTop: i.top - o.top,
995
- containerRect: o,
996
- originalClasses: n
997
- }, e.style.left = `${this._dragState.initialLeft}px`, e.style.top = `${this._dragState.initialTop}px`, e.style.width = `${i.width}px`, e.style.position = "absolute", e.classList.remove(...n), e.classList.add("dragging"), this._dragHandlers = {
998
- move: this._handleDragMove.bind(this),
999
- stop: this._handleDragStop.bind(this)
1000
- }, document.addEventListener("mousemove", this._dragHandlers.move), document.addEventListener("mouseup", this._dragHandlers.stop);
1001
- }
1002
- _handleDragMove(t) {
1003
- if (!this._dragState) return;
1004
- const { element: s, startX: e, startY: i, initialLeft: o, initialTop: n, containerRect: a } = this._dragState, l = t.clientX - e, d = t.clientY - i;
1005
- let c = o + l, h = n + d;
1006
- const f = a.width - s.offsetWidth, g = a.height - s.offsetHeight;
1007
- c = Math.max(0, Math.min(c, f)), h = Math.max(0, Math.min(h, g)), s.style.left = `${c}px`, s.style.top = `${h}px`;
1008
- }
1009
- _handleDragStop(t) {
1010
- if (!this._dragState) return;
1011
- const { element: s, startX: e, startY: i, originalClasses: o } = this._dragState, n = Math.abs(t.clientX - e), a = Math.abs(t.clientY - i), l = n > 5 || a > 5;
1012
- if (s.classList.remove("dragging"), s.style.zIndex = "", l) {
1013
- const d = (c) => {
1014
- c.preventDefault(), c.stopPropagation();
1015
- };
1016
- s.addEventListener("click", d, {
1017
- capture: !0,
1018
- once: !0
1019
- }), this._snapToGrid(s);
1020
- } else
1021
- s.style.position = "", s.style.left = "", s.style.top = "", s.style.width = "", s.classList.add(...o);
1022
- this._saveLayout && this._saveLayout(), document.removeEventListener("mousemove", this._dragHandlers.move), document.removeEventListener("mouseup", this._dragHandlers.stop), this._dragState = null;
1023
- }
1024
- _snapToGrid(t) {
1025
- const s = this._root.getBoundingClientRect(), e = t.getBoundingClientRect();
1026
- let i = 2;
1027
- window.innerWidth >= 1280 ? i = 10 : window.innerWidth >= 1024 ? i = 8 : window.innerWidth >= 768 && (i = 6);
1028
- const o = 3, n = s.width / i, a = s.height / o, l = e.left - s.left + e.width / 2, d = e.top - s.top + e.height / 2;
1029
- let c = Math.floor(l / n) + 1, h = Math.floor(d / a) + 1;
1030
- c = Math.max(1, Math.min(c, i)), h = Math.max(1, Math.min(h, o));
1031
- const f = `col-start-${c}`, g = `row-start-${h}`;
1032
- if (Array.from(
1033
- /** @type {NodeListOf<HTMLElement>} */
1034
- this._root.querySelectorAll(".desktop-icon")
1035
- ).find(
1036
- (p) => p !== t && p.classList.contains(f) && p.classList.contains(g)
1037
- )) {
1038
- if (t.style.position = "", t.style.left = "", t.style.top = "", t.style.width = "", this._dragState && this._dragState.originalClasses) {
1039
- const p = Array.from(t.classList).filter(
1040
- (S) => S.startsWith("col-start-") || S.startsWith("row-start-") || S.startsWith("col-end-")
1041
- );
1042
- t.classList.remove(...p), t.classList.add(...this._dragState.originalClasses);
1043
- }
1044
- return;
1045
- }
1046
- t.style.position = "", t.style.left = "", t.style.top = "", t.style.width = "";
1047
- const m = Array.from(t.classList).filter(
1048
- (p) => p.startsWith("col-start-") || p.startsWith("row-start-") || p.startsWith("col-end-")
1049
- );
1050
- t.classList.remove(...m), t.classList.add(f, g);
1051
- }
1052
- _saveLayout() {
1053
- const t = [];
1054
- /** @type {NodeListOf<HTMLElement>} */
1055
- this._root.querySelectorAll(".desktop-icon").forEach((i) => {
1056
- const o = i.dataset.modal;
1057
- if (o) {
1058
- const n = Array.from(i.classList).filter(
1059
- (a) => a.startsWith("col-start-") || a.startsWith("row-start-")
1060
- );
1061
- t.push({
1062
- id: o,
1063
- classes: n
1064
- });
322
+ #i() {
323
+ const t = this._root.getAttribute("nd-icons");
324
+ if (!t) return;
325
+ const [e, s] = t.split(":").map(Number), i = this._queryAll("[nd-icon]");
326
+ i.forEach((o) => {
327
+ const r = o.getAttribute("nd-icon");
328
+ let n = z(r);
329
+ if (!n) return;
330
+ let c = Math.max(1, Math.min(n.col, e)), d = Math.max(1, Math.min(n.row, s)), u = `${c}:${d}`;
331
+ if (C(i, o, u)) {
332
+ u = X(i, u, e, s);
333
+ const h = z(u);
334
+ c = h.col, d = h.row;
1065
335
  }
336
+ o.style.gridColumnStart = c.toString(), o.style.gridRowStart = d.toString(), o.setAttribute("nd-icon", u);
1066
337
  });
1067
- const e = this._getStorageKey();
1068
- this._storage.set(e, t);
1069
338
  }
339
+ _handleStartDrag(t, e) {
340
+ if (t.button !== 0) return;
341
+ t.preventDefault();
342
+ const s = 200, i = 40, o = t.clientX, r = t.clientY;
343
+ let n, c, d, u, h, p = !1;
344
+ const g = setTimeout(() => {
345
+ u = (w) => {
346
+ const S = w.clientX - o, x = w.clientY - r;
347
+ !p && Math.hypot(S, x) < i || (p || (p = !0, n = e.cloneNode(!0), n.classList.add("nd-icon-ghost"), n.removeAttribute("nd-icon"), document.body.appendChild(n), e.classList.add("is-dragging"), c = o, d = r, n.style.left = `${c}px`, n.style.top = `${d}px`), c = w.clientX, d = w.clientY, n.style.left = `${c}px`, n.style.top = `${d}px`);
348
+ }, h = () => {
349
+ if (document.removeEventListener("mousemove", u), document.removeEventListener("mouseup", h), n && n.remove(), e.classList.remove("is-dragging"), !p || c === void 0) return;
350
+ const w = this._root.getBoundingClientRect(), S = getComputedStyle(this._root), x = {
351
+ cols: parseInt(S.getPropertyValue("--nd-cols"), 10),
352
+ rows: parseInt(S.getPropertyValue("--nd-rows"), 10)
353
+ }, { posString: L } = F(c, d, w, x), $ = this._queryAll("[nd-icon]");
354
+ C($, e, L) || (e.setAttribute("nd-icon", L), this.#i(), this.#o());
355
+ }, document.addEventListener("mousemove", u), document.addEventListener("mouseup", h);
356
+ }, s), m = () => clearTimeout(g);
357
+ document.addEventListener("mouseup", m, { once: !0 });
358
+ }
359
+ #o() {
360
+ const t = {};
361
+ this._queryAll("[nd-icon]").forEach((e) => {
362
+ const s = e.getAttribute("nd-id");
363
+ s && (t[s] = e.getAttribute("nd-icon"));
364
+ }), O.set(this._key, t);
365
+ }
366
+ }
367
+ class l {
368
+ /**
369
+ * Cache parsed JSON states by element to avoid repeated JSON.parse calls.
370
+ * @private
371
+ */
372
+ static #e = /* @__PURE__ */ new WeakMap();
373
+ // --- PERSISTENCE (Geometry & Dataset) ---
374
+ /**
375
+ * Captures the current geometry of a window element.
376
+ *
377
+ * @param {HTMLElement} winElement - The window element to measure.
378
+ * @param {Object} [options={}] - Options like { includePosition: boolean }.
379
+ * @returns {Object} Geometry object { width, height, left, top }.
380
+ */
381
+ static capture(t, e = {}) {
382
+ const s = e.includePosition === !0;
383
+ let i = null;
384
+ const o = () => (i || (i = window.getComputedStyle(t)), i), r = (t.offsetWidth > 0 ? l.#s(t.offsetWidth) : "") || l.#t(t.style.width) || l.#t(o().width), n = (t.offsetHeight > 0 ? l.#s(t.offsetHeight) : "") || l.#t(t.style.height) || l.#t(o().height);
385
+ let c = "", d = "";
386
+ return s && (c = l.#s(t.offsetLeft) || l.#t(t.style.left) || l.#t(o().left), d = l.#s(t.offsetTop) || l.#t(t.style.top) || l.#t(o().top)), { width: r || "", height: n || "", left: c, top: d };
387
+ }
388
+ /**
389
+ * Saves the current window geometry to a dataset attribute.
390
+ *
391
+ * @param {HTMLElement} winElement - The window element.
392
+ * @param {string} [key="prevState"] - The dataset key to use.
393
+ * @param {Object} [options={}] - Capture options.
394
+ * @returns {Object} The saved geometry.
395
+ */
396
+ static save(t, e = "prevState", s = {}) {
397
+ const i = l.capture(t, s), o = JSON.stringify(i);
398
+ return t.dataset[e] = o, l.#i(t).set(e, { raw: o, parsed: i }), i;
399
+ }
400
+ /**
401
+ * Reads a saved window geometry from a dataset attribute.
402
+ *
403
+ * @param {HTMLElement} winElement - The window element.
404
+ * @param {string} [key="prevState"] - The dataset key to read.
405
+ * @returns {Object|null} The parsed geometry or null.
406
+ */
407
+ static read(t, e = "prevState") {
408
+ const s = t.dataset[e];
409
+ if (!s) return null;
410
+ const i = l.#i(t).get(e);
411
+ if (i && i.raw === s)
412
+ return i.parsed;
413
+ try {
414
+ const o = JSON.parse(s);
415
+ if (!o || typeof o != "object") return null;
416
+ const r = {
417
+ width: l.#t(o.width),
418
+ height: l.#t(o.height),
419
+ left: l.#t(o.left),
420
+ top: l.#t(o.top)
421
+ };
422
+ return l.#i(t).set(e, { raw: s, parsed: r }), r;
423
+ } catch {
424
+ return null;
425
+ }
426
+ }
427
+ /**
428
+ * Applies a geometry state back to a window element's styles.
429
+ *
430
+ * @param {HTMLElement} winElement - The window element.
431
+ * @param {Object} state - The geometry state to apply.
432
+ * @param {Object} [options={}] - Options like { includePosition: boolean }.
433
+ * @returns {boolean} True if state was applied.
434
+ */
435
+ static apply(t, e, s = {}) {
436
+ return !e || typeof e != "object" ? !1 : (e.width && t.style.width !== e.width && (t.style.width = e.width), e.height && t.style.height !== e.height && (t.style.height = e.height), s.includePosition && (e.left && t.style.left !== e.left && (t.style.left = e.left), e.top && t.style.top !== e.top && (t.style.top = e.top)), !0);
437
+ }
438
+ /**
439
+ * Ensures that a restoration state exists for the window.
440
+ */
441
+ static ensureRestoreState(t) {
442
+ const e = l.read(t);
443
+ return e?.width && e?.height ? e : l.save(t, "prevState", { includePosition: !1 });
444
+ }
445
+ // --- GEOMETRY (Layout & Ratios) ---
446
+ /**
447
+ * Positions a window using a cascade effect relative to the number of existing windows.
448
+ */
449
+ static positionWindow(t, e, s) {
450
+ const i = t.offsetWidth || parseInt(t.style.width) || s.defaultWidth, o = t.offsetHeight || parseInt(t.style.height) || s.defaultHeight, r = window.innerWidth, n = window.innerHeight, c = e, d = c * s.cascadeOffset, u = c * s.cascadeOffset;
451
+ let h = (r - i) / 2 + d, p = (n - o) / 2 + u;
452
+ const g = s.minMargin;
453
+ h + i > r && (h = Math.max(g, r - i - g)), p + o > n && (p = Math.max(g, n - o - g)), t.style.left = `${Math.round(h)}px`, t.style.top = `${Math.round(p)}px`, l.savePositionRatios(t);
454
+ }
455
+ /**
456
+ * Stabilizes a window's placement after initial rendering to account for dynamic content sizing.
457
+ */
458
+ static stabilizeInitialPlacement(t, e, s) {
459
+ if (!t?.isConnected) return;
460
+ const i = Number.isFinite(s.layoutStabilizationMs) && s.layoutStabilizationMs > 0 ? s.layoutStabilizationMs : 450, o = typeof performance < "u" ? () => performance.now() : () => Date.now(), r = o();
461
+ let n = !0, c = null, d = t.offsetWidth, u = t.offsetHeight;
462
+ const h = () => {
463
+ n && (n = !1, c && (c.disconnect(), c = null));
464
+ }, p = () => {
465
+ if (!n || !t.isConnected) {
466
+ h();
467
+ return;
468
+ }
469
+ if (t.classList.contains("tiled") || t.classList.contains("maximized"))
470
+ return;
471
+ const m = t.offsetWidth, w = t.offsetHeight;
472
+ (m !== d || w !== u) && (d = m, u = w, l.positionWindow(t, e, s));
473
+ }, g = () => {
474
+ if (n) {
475
+ if (p(), o() - r < i) {
476
+ requestAnimationFrame(g);
477
+ return;
478
+ }
479
+ h();
480
+ }
481
+ };
482
+ requestAnimationFrame(g), typeof ResizeObserver == "function" && (c = new ResizeObserver(() => p()), c.observe(t)), setTimeout(() => h(), i);
483
+ }
484
+ /**
485
+ * Calculates and saves the window's center position as ratios relative to the viewport.
486
+ */
487
+ static savePositionRatios(t) {
488
+ if (t.classList.contains("tiled") || t.classList.contains("maximized")) return;
489
+ const e = t.offsetLeft + t.offsetWidth / 2, s = t.offsetTop + t.offsetHeight / 2;
490
+ t.dataset.xRatio = String(e / window.innerWidth), t.dataset.yRatio = String(s / window.innerHeight);
491
+ }
492
+ /**
493
+ * Repositions a window based on previously saved coordinate ratios.
494
+ */
495
+ static repositionWindowFromRatios(t, e, s, i = null) {
496
+ const o = parseFloat(t.dataset.xRatio), r = parseFloat(t.dataset.yRatio);
497
+ if (isNaN(o) || isNaN(r)) return !1;
498
+ const n = (i && i.widthPx > 0 ? i.widthPx : null) || t.offsetWidth, c = (i && i.heightPx > 0 ? i.heightPx : null) || t.offsetHeight, d = o * e, u = r * s;
499
+ return t.style.left = `${Math.round(d - n / 2)}px`, t.style.top = `${Math.round(u - c / 2)}px`, !0;
500
+ }
501
+ // --- SCROLL STATE ---
502
+ /**
503
+ * Captures the current scroll position of the window root and all scrollable descendants.
504
+ */
505
+ static captureScrollState(t) {
506
+ const e = /* @__PURE__ */ new Map();
507
+ return (t.scrollTop > 0 || t.scrollLeft > 0) && e.set("root", { top: t.scrollTop, left: t.scrollLeft }), t.querySelectorAll("*").forEach((s) => {
508
+ (s.scrollTop > 0 || s.scrollLeft > 0) && e.set(l.getElementPath(t, s), { top: s.scrollTop, left: s.scrollLeft });
509
+ }), e;
510
+ }
511
+ /**
512
+ * Restores scroll positions from a captured state Map.
513
+ */
514
+ static restoreScrollState(t, e, s) {
515
+ const i = () => {
516
+ e.forEach((n, c) => {
517
+ let d;
518
+ if (c === "root")
519
+ d = t;
520
+ else
521
+ try {
522
+ d = t.querySelector(`:scope > ${c}`);
523
+ } catch {
524
+ d = t.querySelector(c);
525
+ }
526
+ d && (d.scrollTop = n.top, d.scrollLeft = n.left);
527
+ });
528
+ };
529
+ i();
530
+ const o = t.querySelector(".window-content-scrollable > div") || t, r = new ResizeObserver(() => i());
531
+ r.observe(o), setTimeout(() => r.disconnect(), s.scrollRestoreTimeoutMs);
532
+ }
533
+ // --- HELPERS ---
534
+ /**
535
+ * Helper to parse CSS pixel values.
536
+ */
537
+ static parseCssPixelValue(t) {
538
+ if (!t) return null;
539
+ const e = parseFloat(t);
540
+ return Number.isFinite(e) ? e : null;
541
+ }
542
+ /**
543
+ * Generates a unique selector path for a child element.
544
+ */
545
+ static getElementPath(t, e) {
546
+ let s = [], i = e;
547
+ for (; i && i !== t; ) {
548
+ let o = Array.prototype.indexOf.call(i.parentNode.children, i);
549
+ s.unshift(`${i.tagName}:nth-child(${o + 1})`), i = i.parentNode;
550
+ }
551
+ return s.join(" > ");
552
+ }
553
+ /**
554
+ * Internal helper to normalize CSS values.
555
+ * @private
556
+ */
557
+ static #t(t) {
558
+ return !t || t === "auto" || t === "normal" ? "" : t;
559
+ }
560
+ /**
561
+ * Internal helper to convert numbers to pixel strings.
562
+ * @private
563
+ */
564
+ static #s(t) {
565
+ return Number.isFinite(t) ? `${Math.round(t)}px` : "";
566
+ }
567
+ /**
568
+ * Internal helper to retrieve or initialize the cache for an element.
569
+ * @private
570
+ */
571
+ static #i(t) {
572
+ let e = l.#e.get(t);
573
+ return e || (e = /* @__PURE__ */ new Map(), l.#e.set(t, e)), e;
574
+ }
575
+ }
576
+ const P = {
577
+ root: typeof document < "u" ? document : null,
578
+ modalContainer: "#target",
579
+ pendingModalDatasetKey: "pendingModal",
580
+ registry: [],
581
+ refreshMap: null,
582
+ refreshTimeout: 200,
583
+ windowManager: {
584
+ zIndexBase: 40,
585
+ layoutStabilizationMs: 450,
586
+ cascadeOffset: 30,
587
+ cooldownMs: 500,
588
+ maxWindows: 10,
589
+ snapGap: 6,
590
+ taskbarHeight: 64,
591
+ snapThreshold: 30,
592
+ dragThreshold: 10,
593
+ resizeDebounceMs: 6,
594
+ animationDurationMs: 400,
595
+ defaultWidth: 800,
596
+ defaultHeight: 600,
597
+ minMargin: 10,
598
+ edgeDetectionRatio: 0.4,
599
+ scrollRestoreTimeoutMs: 2e3
600
+ }
601
+ };
602
+ class y {
603
+ /**
604
+ * Detects the snap zone based on current mouse coordinates.
605
+ *
606
+ * @param {Object} config - Configuration object (snapThreshold, edgeDetectionRatio).
607
+ * @param {number} x - Mouse X coordinate.
608
+ * @param {number} y - Mouse Y coordinate.
609
+ * @param {Object} view - Viewport dimensions.
610
+ * @returns {string|null} The snap zone identifier (tl, tr, bl, br, left, right, maximize) or null.
611
+ */
612
+ static detectSnapZone(t, e, s, i) {
613
+ const o = t.snapThreshold, r = i.w * t.edgeDetectionRatio, n = i.h * t.edgeDetectionRatio;
614
+ let c = null;
615
+ return s < o ? e < r ? c = "tl" : e > i.w - r ? c = "tr" : c = "maximize" : e < o ? s < n ? c = "tl" : s > i.h - n ? c = "bl" : c = "left" : e > i.w - o ? s < n ? c = "tr" : s > i.h - n ? c = "br" : c = "right" : s > i.h - o && (c = e < i.w / 2 ? "bl" : "br"), c;
616
+ }
617
+ /**
618
+ * Snaps a window to a specific zone and saves the previous state for restoration.
619
+ *
620
+ * @param {HTMLElement} winElement - The window element.
621
+ * @param {string} type - The snap zone type.
622
+ * @param {Object} config - Configuration object.
623
+ * @param {Object} view - Viewport dimensions.
624
+ */
625
+ static snapWindow(t, e, s, i) {
626
+ t.classList.contains("tiled") || l.ensureRestoreState(t), t.classList.add("window-toggling", "tiled"), t.dataset.snapType = e;
627
+ const o = y.getSnapLayout(e, s, i.w, i.h);
628
+ Object.assign(t.style, o), setTimeout(() => t.classList.remove("window-toggling"), s.animationDurationMs);
629
+ }
630
+ /**
631
+ * Calculates the CSS layout properties for a specific snap zone.
632
+ *
633
+ * @param {string} type - Snap zone type.
634
+ * @param {Object} config - Configuration object.
635
+ * @param {number} vw - Viewport width.
636
+ * @param {number} vh - Viewport height.
637
+ * @returns {Object} CSS properties (width, height, top, left).
638
+ */
639
+ static getSnapLayout(t, e, s, i) {
640
+ const o = e.snapGap, r = (s - o * 3) / 2, n = (i - o * 3) / 2, c = i - o * 2, d = o, u = r + o * 2, h = o, p = n + o * 2, m = {
641
+ tl: { top: h, left: d, width: r, height: n },
642
+ tr: { top: h, left: u, width: r, height: n },
643
+ bl: { top: p, left: d, width: r, height: n },
644
+ br: { top: p, left: u, width: r, height: n },
645
+ left: { top: h, left: d, width: r, height: c },
646
+ right: { top: h, left: u, width: r, height: c }
647
+ }[t];
648
+ return m ? {
649
+ width: `${m.width}px`,
650
+ height: `${m.height}px`,
651
+ top: `${m.top}px`,
652
+ left: `${m.left}px`
653
+ } : {};
654
+ }
655
+ /**
656
+ * Adjusts all tiled windows to the new viewport size.
657
+ * Called during window resize events.
658
+ *
659
+ * @param {Map} windows - Map of all windows.
660
+ * @param {Object} config - Configuration object.
661
+ * @param {Object} callbacks - Hooks (repositionFromRatios).
662
+ */
663
+ static handleResize(t, e, s) {
664
+ const i = window.innerWidth, o = window.innerHeight, r = o - e.taskbarHeight;
665
+ t.forEach((n) => {
666
+ if (n.classList.contains("tiled") && n.dataset.snapType) {
667
+ const c = n.dataset.snapType, d = y.getSnapLayout(c, e, i, r);
668
+ Object.assign(n.style, d);
669
+ } else n.classList.contains("maximized") || s.repositionFromRatios(n, i, o);
670
+ });
671
+ }
672
+ /**
673
+ * Restores a window to its free-floating state with original dimensions.
674
+ *
675
+ * @param {HTMLElement} winElement - The window element.
676
+ * @param {number|null} xRatio - Optional X ratio for centered restoration during drag.
677
+ * @param {Object} config - Configuration object.
678
+ * @param {Object} callbacks - Hooks (onUpdateMaximizeIcon, onSavePositionRatios).
679
+ */
680
+ static restoreWindowInternal(t, e, s, i) {
681
+ let o, r;
682
+ const n = l.read(t);
683
+ e === null ? n && (o = n.width, r = n.height) : (o = n?.width || t.style.width, r = n?.height || t.style.height), (!o || o === "100%") && (o = s.defaultWidth + "px"), (!r || r === "100%") && (r = s.defaultHeight + "px"), t.classList.remove("maximized", "tiled"), i.onUpdateMaximizeIcon(t, !1), t.classList.add("window-toggling", "dragging-restore"), l.apply(t, { width: o, height: r }), setTimeout(() => {
684
+ t.classList.remove("window-toggling", "dragging-restore"), i.onSavePositionRatios(t);
685
+ }, s.animationDurationMs);
686
+ }
687
+ }
688
+ const Y = "[nd-toast-stack]", j = /* @__PURE__ */ new Set(["info", "success", "error", "warning"]), B = /* @__PURE__ */ new Set([
689
+ "top-right",
690
+ "top-left",
691
+ "bottom-right",
692
+ "bottom-left"
693
+ ]), U = 220, A = {
694
+ duration: 3e3,
695
+ closable: !0
696
+ }, D = (a) => typeof a == "string" && B.has(a), N = (a) => {
697
+ const t = Number.isFinite(a?.duration) ? Number(a.duration) : A.duration, e = typeof a?.closable == "boolean" ? a.closable : A.closable, s = D(a?.position) ? a.position : null;
698
+ return {
699
+ duration: t,
700
+ closable: e,
701
+ position: s
702
+ };
703
+ }, Z = (a) => {
704
+ if (typeof document > "u") return null;
705
+ let t = (
706
+ /** @type {HTMLElement|null} */
707
+ document.querySelector(Y)
708
+ );
709
+ if (!t)
710
+ return console.warn(
711
+ '[nidamjs:toast] Missing [nd-toast-stack]. Add <div nd-toast-stack data-position="bottom-right"></div> if you want to display toasts.'
712
+ ), null;
713
+ const e = t.getAttribute("data-position"), s = a || (D(e) ? e : "top-right");
714
+ return t.setAttribute("data-position", s), t;
715
+ }, M = (a) => {
716
+ if (typeof a == "string")
717
+ return a.trim() ? [a] : [];
718
+ if (Array.isArray(a))
719
+ return a.filter(Boolean).flatMap((t) => typeof t == "object" && t !== null ? M(t) : String(t).trim()).filter(Boolean);
720
+ if (a && typeof a == "object") {
721
+ const t = (
722
+ /** @type {Record<string, unknown>} */
723
+ a
724
+ );
725
+ return t.errors ? M(t.errors) : Object.values(t).flatMap((e) => M(e)).filter(Boolean);
726
+ }
727
+ return [];
728
+ }, G = (a) => {
729
+ const t = document.createElement("div");
730
+ if (t.className = "nd-toast-content", a.length <= 1) {
731
+ const s = document.createElement("span");
732
+ return s.textContent = a[0], t.appendChild(s), t;
733
+ }
734
+ const e = document.createElement("ul");
735
+ return e.className = "nd-toast-list", a.forEach((s) => {
736
+ const i = document.createElement("li");
737
+ i.textContent = s, e.appendChild(i);
738
+ }), t.appendChild(e), t;
739
+ }, H = (a, t) => {
740
+ t.setAttribute("data-state", "closing"), window.setTimeout(() => {
741
+ t.remove(), a.childElementCount || a.remove();
742
+ }, U);
743
+ }, J = (a, t, e) => {
744
+ const s = document.createElement("button");
745
+ return s.type = "button", s.className = "nd-toast-close", s.setAttribute("aria-label", "Close notification"), s.textContent = "x", s.addEventListener("click", () => {
746
+ e && window.clearTimeout(e), H(a, t);
747
+ }), s;
748
+ }, K = (a, t, e) => e <= 0 ? 0 : window.setTimeout(() => {
749
+ H(a, t);
750
+ }, e), Q = (a, t, e = void 0) => {
751
+ if (typeof window > "u" || typeof document > "u")
752
+ return;
753
+ const s = M(t);
754
+ if (!s.length) return;
755
+ const i = j.has(a) ? a : "info", o = N(e), r = Z(o.position);
756
+ if (!r) return;
757
+ const n = document.createElement("div");
758
+ n.className = "nd-toast", n.setAttribute("data-type", i), n.setAttribute("data-state", "open"), n.appendChild(G(s));
759
+ const c = K(r, n, o.duration);
760
+ return o.closable && n.appendChild(J(r, n, c)), r.appendChild(n), o.duration;
761
+ }, E = (a) => a === "error" ? "error" : a === "warn" || a === "warning" ? "warning" : a === "success" ? "success" : "info", tt = (a = {}) => {
762
+ const t = N(a);
763
+ return (e, s) => {
764
+ const i = E(e);
765
+ Q(i, s, t);
766
+ };
767
+ }, T = tt();
768
+ class f {
769
+ // --- PUBLIC API ---
770
+ /**
771
+ * Opens a window for a given endpoint.
772
+ * Orchestrates validation, content fetching, and UI initialization.
773
+ */
774
+ static async open(t, e, s) {
775
+ const { pendingRequests: i } = s, o = f._getValidationError(
776
+ t,
777
+ e,
778
+ s
779
+ );
780
+ if (o) {
781
+ if (o === "ALREADY_OPEN")
782
+ return s.windows.get(t);
783
+ if (o === "COOLDOWN") return Promise.resolve();
784
+ throw new Error(o);
785
+ }
786
+ if (i.has(t))
787
+ return i.get(t);
788
+ const r = (async () => {
789
+ try {
790
+ const n = await s.fetchWindowContent(t, e);
791
+ if (typeof n != "string")
792
+ throw new TypeError("HTML content must be a string");
793
+ const c = s.windows.get(t);
794
+ return c && e.force ? f._refreshExisting(
795
+ c,
796
+ n,
797
+ s,
798
+ e
799
+ ) : f._createAndSetup(t, n, s, e);
800
+ } catch (n) {
801
+ throw f._handleError(n, s), n;
802
+ } finally {
803
+ i.delete(t);
804
+ }
805
+ })();
806
+ return i.set(t, r), r;
807
+ }
808
+ /**
809
+ * Closes a window with an animation.
810
+ */
811
+ static close(t, e) {
812
+ const s = t.dataset.endpoint;
813
+ e.get(s) === t && e.delete(s), t.classList.add("animate-disappearance"), t.classList.remove("animate-appearance"), t.addEventListener(
814
+ "animationend",
815
+ () => {
816
+ t.isConnected && t.remove();
817
+ },
818
+ { once: !0 }
819
+ );
820
+ }
821
+ /**
822
+ * Toggles the maximization state of a window.
823
+ */
824
+ static toggleMaximize(t, e) {
825
+ const { config: s, callbacks: i } = e, o = t.classList.contains("maximized"), r = t.classList.contains("tiled") && t.dataset.snapType;
826
+ t.classList.add("window-toggling"), !o && !t.classList.contains("tiled") && i.saveWindowState(t, "prevState", {
827
+ includePosition: !1
828
+ });
829
+ const n = t.classList.toggle("maximized");
830
+ let c = !1;
831
+ if (f.updateMaximizeIcon(t, n), !n)
832
+ if (r) {
833
+ const d = y.getSnapLayout(
834
+ t.dataset.snapType,
835
+ s,
836
+ window.innerWidth,
837
+ window.innerHeight - s.taskbarHeight
838
+ );
839
+ Object.assign(t.style, d);
840
+ } else {
841
+ const d = i.readWindowState(t);
842
+ i.applyWindowState(t, d);
843
+ const u = {
844
+ widthPx: l.parseCssPixelValue(d?.width) || t.offsetWidth,
845
+ heightPx: l.parseCssPixelValue(d?.height) || t.offsetHeight
846
+ };
847
+ l.repositionWindowFromRatios(
848
+ t,
849
+ window.innerWidth,
850
+ window.innerHeight,
851
+ u
852
+ ), c = !0;
853
+ }
854
+ setTimeout(() => {
855
+ t.classList.remove("window-toggling"), c && l.savePositionRatios(t);
856
+ }, s.animationDurationMs);
857
+ }
858
+ /**
859
+ * Focuses a window and brings it to the front.
860
+ */
861
+ static focusWindow(t, e) {
862
+ e.zIndexCounter++, t.style.zIndex = e.zIndexCounter, t.classList.add("focused"), e.windows.forEach((s) => {
863
+ s !== t && s.classList.remove("focused");
864
+ });
865
+ }
866
+ /**
867
+ * Checks if a window is marked as busy.
868
+ */
869
+ static isWindowBusy(t) {
870
+ return t.dataset.isBusy === "true" || t.querySelector('[data-is-busy="true"]') !== null;
871
+ }
872
+ /**
873
+ * Toggles the maximize/compress icon.
874
+ */
875
+ static updateMaximizeIcon(t, e) {
876
+ const s = t.querySelector("[data-maximize] i");
877
+ s && (s.classList.toggle("fa-expand", !e), s.classList.toggle("fa-compress", e));
878
+ }
879
+ /**
880
+ * Finds and closes the focused/topmost window.
881
+ */
882
+ static closeTopmostWindow(t) {
883
+ let e = null, s = 0;
884
+ t.forEach((i) => {
885
+ if (i.classList.contains("animate-disappearance")) return;
886
+ const o = parseInt(i.style.zIndex || 0, 10);
887
+ o > s && (s = o, e = i);
888
+ }), e && f.close(e, t);
889
+ }
890
+ // --- PRIVATE HELPERS ---
891
+ /**
892
+ * Validates if a window can be opened.
893
+ * @private
894
+ */
895
+ static _getValidationError(t, e, s) {
896
+ const { windows: i, config: o, lastOpenTimestamps: r } = s;
897
+ if (i.size >= o.maxWindows && !i.has(t)) {
898
+ const c = document.body.dataset.errorMaxWindows || `Maximum of ${o.maxWindows} windows allowed.`;
899
+ return T("error", c.replace("%s", String(o.maxWindows))), "MAX_WINDOWS_REACHED";
900
+ }
901
+ if (i.has(t) && !e.force)
902
+ return e.activate && f.focusWindow(i.get(t), s), "ALREADY_OPEN";
903
+ const n = Date.now();
904
+ return !e.force && n - (r.get(t) || 0) < o.cooldownMs ? "COOLDOWN" : (r.set(t, n), null);
905
+ }
906
+ /**
907
+ * Handles refreshing an existing window.
908
+ * @private
909
+ */
910
+ static _refreshExisting(t, e, s, i) {
911
+ return !i.activate && f.isWindowBusy(t) || (f._applyNewContent(t, e, s), i.activate && f.focusWindow(t, s), i.focusSelector && f.handleFocusSelector(t, i.focusSelector)), t;
912
+ }
913
+ /**
914
+ * Handles creating and initializing a new window.
915
+ * @private
916
+ */
917
+ static _createAndSetup(t, e, s, i) {
918
+ const o = f._parseHTML(e);
919
+ if (!o)
920
+ throw new Error(`No .window element found in content for ${t}`);
921
+ return o.dataset.endpoint = t, f._initializeNewWindow(
922
+ o,
923
+ t,
924
+ i,
925
+ s
926
+ ), o;
927
+ }
928
+ /**
929
+ * Parses HTML string into a DOM element.
930
+ * @private
931
+ */
932
+ static _parseHTML(t) {
933
+ return new DOMParser().parseFromString(t, "text/html").querySelector(".window");
934
+ }
935
+ /**
936
+ * Applies new inner HTML to an existing window while preserving state.
937
+ * @private
938
+ */
939
+ static _applyNewContent(t, e, s) {
940
+ const i = f._parseHTML(e);
941
+ if (!i) return;
942
+ const { config: o, callbacks: r } = s, n = {
943
+ snapType: t.dataset.snapType,
944
+ xRatio: t.dataset.xRatio,
945
+ yRatio: t.dataset.yRatio,
946
+ isFocused: t.classList.contains("focused"),
947
+ isMaximized: t.classList.contains("maximized"),
948
+ isTiled: t.classList.contains("tiled"),
949
+ scroll: l.captureScrollState(t)
950
+ };
951
+ t.innerHTML = i.innerHTML, t.className = i.className, n.snapType && (t.dataset.snapType = n.snapType), n.xRatio && (t.dataset.xRatio = n.xRatio), n.yRatio && (t.dataset.yRatio = n.yRatio), n.isFocused && t.classList.add("focused"), n.isTiled && t.classList.add("tiled"), n.isMaximized && (t.classList.add("maximized"), f.updateMaximizeIcon(t, !0)), !n.isTiled && !n.isMaximized && (i.style.width && (t.style.width = i.style.width), i.style.height && (t.style.height = i.style.height)), l.restoreScrollState(t, n.scroll, o), r.initializeContent(t);
952
+ }
953
+ /**
954
+ * Initial setup for a fresh window element.
955
+ * @private
956
+ */
957
+ static _initializeNewWindow(t, e, s, i) {
958
+ const { root: o, windows: r, config: n, callbacks: c } = i;
959
+ Object.assign(t.style, {
960
+ position: "absolute",
961
+ pointerEvents: "auto",
962
+ margin: "0",
963
+ visibility: "hidden"
964
+ }), o.appendChild(t);
965
+ const d = t.dataset.defaultSnap;
966
+ d ? y.snapWindow(t, d, n, {
967
+ w: window.innerWidth,
968
+ h: window.innerHeight - n.taskbarHeight
969
+ }) : l.positionWindow(t, r.size, n), r.set(e, t), c.initializeContent(t), s.activate && f.focusWindow(t, i), t.style.visibility = "", d || l.stabilizeInitialPlacement(
970
+ t,
971
+ r.size - 1,
972
+ n
973
+ ), s.focusSelector && f.handleFocusSelector(t, s.focusSelector);
974
+ }
975
+ /**
976
+ * Focuses an element inside the window.
977
+ */
978
+ static handleFocusSelector(t, e) {
979
+ const s = t.querySelector(e);
980
+ s && ((s.type === "radio" || s.type === "checkbox") && (s.checked = !0), s.focus());
981
+ }
982
+ /**
983
+ * Global error handler for the lifecycle.
984
+ * @private
985
+ */
986
+ static _handleError(t, e) {
987
+ console.error("Window Lifecycle Error:", t);
988
+ const s = document.body.dataset.errorOpenFailed || "Failed to open window.";
989
+ T("error", s);
990
+ }
991
+ }
992
+ class _ {
993
+ /**
994
+ * Initializes the drag process for a window.
995
+ * Attaches mousemove and mouseup listeners to the document.
996
+ *
997
+ * @param {MouseEvent} e - The initial mousedown event.
998
+ * @param {HTMLElement} winElement - The window element to drag.
999
+ * @param {Object} config - Configuration object (dragThreshold, taskbarHeight).
1000
+ * @param {Object} state - Shared drag state object to be populated.
1001
+ * @param {Object} callbacks - Hooks for external actions (onRestore, onMaximize, onSnap, etc.).
1002
+ */
1003
+ static drag(t, e, s, i, o) {
1004
+ if (i.active) return;
1005
+ Object.assign(i, {
1006
+ active: !0,
1007
+ winElement: e,
1008
+ startX: t.clientX,
1009
+ startY: t.clientY,
1010
+ currentX: t.clientX,
1011
+ currentY: t.clientY,
1012
+ startWinLeft: e.offsetLeft,
1013
+ startWinTop: e.offsetTop,
1014
+ isRestored: !1,
1015
+ restoreXRatio: null,
1016
+ initialState: {
1017
+ tiled: e.classList.contains("tiled"),
1018
+ maximized: e.classList.contains("maximized")
1019
+ },
1020
+ view: {
1021
+ w: window.innerWidth,
1022
+ h: window.innerHeight - s.taskbarHeight
1023
+ },
1024
+ snap: null,
1025
+ isDragging: !1
1026
+ });
1027
+ const r = (c) => {
1028
+ i.currentX = c.clientX, i.currentY = c.clientY;
1029
+ }, n = () => _._handleDragStop(s, i, o);
1030
+ i._moveHandler = r, document.addEventListener("mousemove", r, { passive: !0 }), document.addEventListener("mouseup", n, { once: !0 }), requestAnimationFrame(() => _._dragLoop(s, i, o));
1031
+ }
1032
+ /**
1033
+ * Internal animation loop for smooth dragging.
1034
+ * @private
1035
+ */
1036
+ static _dragLoop(t, e, s) {
1037
+ e.active && (_._updateDragPosition(t, e, s), requestAnimationFrame(() => _._dragLoop(t, e, s)));
1038
+ }
1039
+ /**
1040
+ * Core logic for calculating the new window position during a drag.
1041
+ * Handles automatic restoration from maximized/tiled states and snap zone detection.
1042
+ *
1043
+ * @private
1044
+ */
1045
+ static _updateDragPosition(t, e, s) {
1046
+ const { winElement: i, currentX: o, currentY: r, startX: n, startY: c } = e, d = o - n, u = r - c;
1047
+ if (!e.isDragging && (Math.abs(d) > t.dragThreshold || Math.abs(u) > t.dragThreshold) && (e.isDragging = !0), !e.isDragging) return;
1048
+ (e.initialState.tiled || e.initialState.maximized) && !e.isRestored && (e.initialState.maximized ? (e.restoreXRatio = n / window.innerWidth, s.onRestore(i, e.restoreXRatio, !0), e.startWinTop = 0) : (e.restoreXRatio = (n - i.offsetLeft) / i.offsetWidth, s.onRestore(i, null, !1), e.startWinTop = i.offsetTop), e.startX = o, e.startY = r, e.isRestored = !0);
1049
+ let h, p;
1050
+ e.isRestored && e.restoreXRatio !== null ? (h = o - e.restoreXRatio * i.offsetWidth, p = Math.max(0, e.startWinTop + (r - c))) : (h = e.startWinLeft + (e.isRestored ? o - n : d), p = Math.max(0, e.startWinTop + (e.isRestored ? r - c : u))), i.style.left = `${h}px`, i.style.top = `${p}px`, (e.isRestored || !e.initialState.tiled && !e.initialState.maximized) && (i.classList.remove("tiled", "maximized"), s.onUpdateMaximizeIcon(i, !1));
1051
+ const g = s.detectSnapZone(o, r, e.view);
1052
+ e.snap !== g && (e.snap = g, s.updateSnapIndicator(g, e.view));
1053
+ }
1054
+ /**
1055
+ * Finalizes the drag process, removes listeners, and applies final snapping or state saving.
1056
+ * @private
1057
+ */
1058
+ static _handleDragStop(t, e, s) {
1059
+ if (!e.active) return;
1060
+ const { winElement: i, snap: o, view: r } = e;
1061
+ document.removeEventListener("mousemove", e._moveHandler), s.updateSnapIndicator(null, r), o ? o === "maximize" ? s.onMaximize(i) : s.onSnap(i, o, r) : s.onSaveState(i), e.active = !1;
1062
+ }
1063
+ }
1064
+ class b {
1065
+ /**
1066
+ * Loads window content for a given endpoint.
1067
+ *
1068
+ * @param {string} endpoint - The route or identifier.
1069
+ * @param {Object} options - Additional options for the fetch.
1070
+ * @param {Object} context - Context containing flags (isStatic, resolveEndpoint).
1071
+ * @returns {Promise<string>} The HTML content.
1072
+ */
1073
+ static async load(t, e, s) {
1074
+ const { isStatic: i, resolveEndpoint: o } = s;
1075
+ if (i) {
1076
+ const c = b._getStaticTemplateContent(t);
1077
+ if (c !== null)
1078
+ return c;
1079
+ throw new Error(`Static route not found: ${String(t || "")}`);
1080
+ }
1081
+ const r = (o || b._defaultResolveEndpoint)(t), n = await fetch(r, {
1082
+ headers: { "X-Modal-Request": "1" },
1083
+ cache: "no-cache"
1084
+ });
1085
+ if (!n.ok)
1086
+ throw new Error(`Failed to fetch window content: ${n.statusText}`);
1087
+ return n.text();
1088
+ }
1089
+ /**
1090
+ * Default implementation to resolve an endpoint to a URL.
1091
+ *
1092
+ * @param {string} endpoint
1093
+ * @returns {string} The resolved URL.
1094
+ */
1095
+ static _defaultResolveEndpoint(t) {
1096
+ return `/${String(t || "").replace(/^\/+/, "")}`;
1097
+ }
1098
+ /**
1099
+ * Normalizes an endpoint by trimming and removing leading slashes.
1100
+ *
1101
+ * @param {string} endpoint
1102
+ * @returns {string}
1103
+ */
1104
+ static _normalizeEndpoint(t) {
1105
+ return String(t || "").trim().replace(/^\/+/, "");
1106
+ }
1107
+ /**
1108
+ * Builds a list of potential route candidates for static template matching.
1109
+ * (e.g., "team/details.html" -> ["team/details.html", "details.html", "team/details", "details"])
1110
+ *
1111
+ * @param {string} endpoint
1112
+ * @returns {string[]}
1113
+ */
1114
+ static _buildStaticRouteCandidates(t) {
1115
+ const e = b._normalizeEndpoint(t), s = [], i = (r) => {
1116
+ const n = String(r || "").trim();
1117
+ !n || s.includes(n) || s.push(n);
1118
+ };
1119
+ i(e);
1120
+ const o = e.split("/").pop();
1121
+ return i(o), e.endsWith(".html") && i(e.slice(0, -5)), o && o.endsWith(".html") && i(o.slice(0, -5)), s;
1122
+ }
1123
+ /**
1124
+ * Searches for a <template> element with a matching data-route attribute.
1125
+ *
1126
+ * @param {string} endpoint
1127
+ * @returns {string|null} The template content or null if not found.
1128
+ */
1129
+ static _getStaticTemplateContent(t) {
1130
+ const e = document.querySelectorAll("template[data-route]");
1131
+ if (!e.length) return null;
1132
+ const s = /* @__PURE__ */ new Map();
1133
+ e.forEach((o) => {
1134
+ const r = o.getAttribute("data-route");
1135
+ r && s.set(r.trim(), o.innerHTML);
1136
+ });
1137
+ const i = b._buildStaticRouteCandidates(t);
1138
+ for (const o of i)
1139
+ if (s.has(o))
1140
+ return s.get(o);
1141
+ return null;
1142
+ }
1143
+ }
1144
+ class et extends R {
1145
+ _config = { ...P.windowManager };
1146
+ _windows = /* @__PURE__ */ new Map();
1147
+ _zIndexCounter = this._config.zIndexBase;
1148
+ _getModules = null;
1149
+ _fetchWindowContent = null;
1150
+ _initializeContent = null;
1151
+ _resolveEndpoint = null;
1152
+ _static = !1;
1153
+ _lastOpenTimestamps = /* @__PURE__ */ new Map();
1154
+ _pendingRequests = /* @__PURE__ */ new Map();
1155
+ _snapIndicator = null;
1156
+ _dragState = { active: !1 };
1157
+ constructor(t, e, s = {}) {
1158
+ super(t, e);
1159
+ const {
1160
+ getModules: i = null,
1161
+ config: o = null,
1162
+ fetchWindowContent: r = null,
1163
+ initializeContent: n = null,
1164
+ resolveEndpoint: c = null,
1165
+ static: d = !1
1166
+ } = s || {};
1167
+ this._getModules = i, this._initializeContent = n || (() => {
1168
+ }), this._resolveEndpoint = c, this._static = !!d, this._fetchWindowContent = r || ((u, h) => b.load(u, h, {
1169
+ isStatic: this._static,
1170
+ resolveEndpoint: this._resolveEndpoint
1171
+ })), o && typeof o == "object" && (this._config = { ...this._config, ...o }), this._zIndexCounter = this._config.zIndexBase, this._initSnapIndicator(), this._hydrateExistingWindows();
1172
+ }
1173
+ _hydrateExistingWindows() {
1174
+ this._root.querySelectorAll(".window").forEach((e) => {
1175
+ const s = (
1176
+ /** @type {HTMLElement} */
1177
+ e
1178
+ ), i = s.dataset.endpoint || s.dataset.modal;
1179
+ if (i && !this._windows.has(i)) {
1180
+ s.dataset.endpoint || (s.dataset.endpoint = i), this._windows.set(i, s), this._initializeModalContent(s);
1181
+ const o = parseInt(s.style.zIndex || "0", 10);
1182
+ o > this._zIndexCounter && (this._zIndexCounter = o);
1183
+ }
1184
+ });
1185
+ }
1186
+ _initSnapIndicator() {
1187
+ this._snapIndicator = document.createElement("div"), this._snapIndicator.className = "snap-indicator", document.body.appendChild(this._snapIndicator);
1188
+ }
1189
+ _bindEvents() {
1190
+ this._delegator.on("click", "[data-modal]", (e, s) => {
1191
+ s.hasAttribute("nd-taskbar-icon") || (e.preventDefault(), this.open(s.dataset.modal).catch((i) => {
1192
+ i?.message !== "MAX_WINDOWS_REACHED" && console.error("Modal trigger failed:", i);
1193
+ }));
1194
+ }), this._delegator.on("click", "[data-maximize]", (e, s) => {
1195
+ e.preventDefault();
1196
+ const i = s.closest(".window");
1197
+ i && this.toggleMaximize(i);
1198
+ }), this._delegator.on("click", "[data-close]", (e, s) => {
1199
+ e.preventDefault();
1200
+ const i = s.closest(".window");
1201
+ i && this.close(i);
1202
+ }), this._delegator.on("mousedown", ".window", (e, s) => {
1203
+ if (e.target.closest("[data-close]") || e.target.closest("[data-modal]"))
1204
+ return;
1205
+ const i = s.closest(".window");
1206
+ i && this.focus(i);
1207
+ }), this._delegator.on("mousedown", "[data-bar]", (e, s) => {
1208
+ if (e.target.closest("[data-close]") || e.target.closest("[data-maximize]"))
1209
+ return;
1210
+ e.preventDefault();
1211
+ const i = s.closest(".window");
1212
+ i && (this.focus(i), this.drag(e, i));
1213
+ }), this._delegator.on("keydown", null, (e) => {
1214
+ e.key === "Escape" && !e.repeat && this.closeTopmost();
1215
+ });
1216
+ let t;
1217
+ window.addEventListener("resize", () => {
1218
+ clearTimeout(t), t = setTimeout(() => {
1219
+ y.handleResize(this._windows, this._config, {
1220
+ repositionFromRatios: (e, s, i) => l.repositionWindowFromRatios(e, s, i)
1221
+ });
1222
+ }, this._config.resizeDebounceMs);
1223
+ });
1224
+ }
1225
+ _getLifecycleContext() {
1226
+ return {
1227
+ root: this._root,
1228
+ windows: this._windows,
1229
+ config: this._config,
1230
+ zIndexCounter: this._zIndexCounter,
1231
+ pendingRequests: this._pendingRequests,
1232
+ lastOpenTimestamps: this._lastOpenTimestamps,
1233
+ fetchWindowContent: this._fetchWindowContent,
1234
+ callbacks: {
1235
+ initializeContent: (t) => this._initializeModalContent(t),
1236
+ saveWindowState: (t, e, s) => l.save(t, e, s),
1237
+ readWindowState: (t, e) => l.read(t, e),
1238
+ applyWindowState: (t, e, s) => l.apply(t, e, s)
1239
+ }
1240
+ };
1241
+ }
1242
+ async open(t, e = !1, s = null, i = !0) {
1243
+ const o = this._windows.has(t), r = await f.open(
1244
+ t,
1245
+ { force: e, focusSelector: s, activate: i },
1246
+ this._getLifecycleContext()
1247
+ );
1248
+ return o ? i && this._root.dispatchEvent(
1249
+ new CustomEvent("window:focused", {
1250
+ detail: { endpoint: t },
1251
+ bubbles: !0
1252
+ })
1253
+ ) : this._root.dispatchEvent(
1254
+ new CustomEvent("window:opened", {
1255
+ detail: { endpoint: t },
1256
+ bubbles: !0
1257
+ })
1258
+ ), r;
1259
+ }
1260
+ close(t) {
1261
+ const e = t.dataset.endpoint, s = f.close(t, this._windows);
1262
+ return this._root.dispatchEvent(
1263
+ new CustomEvent("window:closed", {
1264
+ detail: { endpoint: e },
1265
+ bubbles: !0
1266
+ })
1267
+ ), s;
1268
+ }
1269
+ closeTopmost() {
1270
+ let t = null, e = -1;
1271
+ return this._windows.forEach((s) => {
1272
+ if (s.classList.contains("animate-disappearance"))
1273
+ return;
1274
+ const i = parseInt(s.style.zIndex || 0, 10);
1275
+ i > e && (e = i, t = s);
1276
+ }), t ? this.close(t) : null;
1277
+ }
1278
+ getWindows() {
1279
+ return Array.from(this._windows.entries());
1280
+ }
1281
+ focus(t) {
1282
+ const e = this._getLifecycleContext(), s = t.dataset.endpoint;
1283
+ f.focusWindow(t, e), this._zIndexCounter = e.zIndexCounter, this._root.dispatchEvent(
1284
+ new CustomEvent("window:focused", {
1285
+ detail: { endpoint: s },
1286
+ bubbles: !0
1287
+ })
1288
+ );
1289
+ }
1290
+ drag(t, e) {
1291
+ const s = {
1292
+ onRestore: (i, o) => y.restoreWindowInternal(i, o, this._config, {
1293
+ onUpdateMaximizeIcon: (r, n) => f.updateMaximizeIcon(r, n),
1294
+ onSavePositionRatios: (r) => l.savePositionRatios(r)
1295
+ }),
1296
+ onUpdateMaximizeIcon: (i, o) => f.updateMaximizeIcon(i, o),
1297
+ detectSnapZone: (i, o, r) => y.detectSnapZone(this._config, i, o, r),
1298
+ updateSnapIndicator: (i, o) => this._updateSnapIndicator(i, o),
1299
+ onMaximize: (i) => this.toggleMaximize(i),
1300
+ onSnap: (i, o, r) => y.snapWindow(i, o, this._config, r),
1301
+ onSaveState: (i) => l.savePositionRatios(i)
1302
+ };
1303
+ return _.drag(
1304
+ t,
1305
+ e,
1306
+ this._config,
1307
+ this._dragState,
1308
+ s
1309
+ );
1310
+ }
1311
+ _updateSnapIndicator(t, e) {
1312
+ if (!this._snapIndicator)
1313
+ return;
1314
+ if (!t) {
1315
+ this._snapIndicator.classList.remove("visible");
1316
+ return;
1317
+ }
1318
+ let s;
1319
+ t === "maximize" ? s = {
1320
+ top: "0px",
1321
+ left: "0px",
1322
+ width: `${e.w}px`,
1323
+ height: `${e.h}px`
1324
+ } : s = y.getSnapLayout(t, this._config, e.w, e.h), Object.assign(this._snapIndicator.style, s), this._snapIndicator.classList.add("visible");
1325
+ }
1326
+ toggleMaximize(t) {
1327
+ return f.toggleMaximize(
1328
+ t,
1329
+ this._getLifecycleContext()
1330
+ );
1331
+ }
1332
+ _initializeModalContent(t) {
1333
+ const e = this._getModules ? this._getModules() : null;
1334
+ this._initializeContent(t, {
1335
+ delegator: this._delegator,
1336
+ modules: e,
1337
+ manager: this
1338
+ });
1339
+ }
1340
+ }
1341
+ class W {
1342
+ /**
1343
+ * Checks if a path matches a given pattern.
1344
+ *
1345
+ * Rules:
1346
+ * 1. Exact Match: "team" matches "team".
1347
+ * 2. Wildcard (*): "team/*" matches "team/5", "team/12/details".
1348
+ * 3. Parameters ({id}): "team/{id}" matches "team/5".
1349
+ *
1350
+ * @param {string} pattern - The pattern from configuration.
1351
+ * @param {string} path - The actual window endpoint path.
1352
+ * @param {string|number|null} [entityId=null] - Optional ID to enforce specific parameter matching.
1353
+ * @returns {boolean}
1354
+ */
1355
+ static match(t, e, s = null) {
1356
+ if (t === e) return !0;
1357
+ const i = t.split("/"), o = e.split("/");
1358
+ return !(i[i.length - 1] === "*") && i.length !== o.length ? !1 : i.every((n, c) => n === "*" ? !0 : n.startsWith("{") && n.endsWith("}") ? s !== null ? String(o[c]) === String(s) : !0 : n === o[c]);
1359
+ }
1360
+ /**
1361
+ * Normalizes an endpoint/path by removing leading slashes and trimming.
1362
+ *
1363
+ * @param {string} path
1364
+ * @returns {string}
1365
+ */
1366
+ static normalize(t) {
1367
+ return String(t || "").trim().replace(/^\/+/, "");
1368
+ }
1369
+ }
1370
+ class st {
1371
+ /** @type {Object} Map of event names to path patterns */
1372
+ #e;
1373
+ /** @type {number} Delay in ms before triggering a refresh/close */
1374
+ #t;
1375
+ /**
1376
+ * @param {Object} windowProvider - An object providing window management methods (getWindows, open, close).
1377
+ * @param {Object} options - Configuration options.
1378
+ */
1379
+ constructor(t, { refreshMap: e = null, refreshTimeout: s = 200 } = {}) {
1380
+ this._provider = t, this.#e = e || window.window_refresh_map || {}, this.#t = s;
1381
+ }
1382
+ /**
1383
+ * Updates the refresh mapping at runtime.
1384
+ */
1385
+ setRefreshMap(t = {}) {
1386
+ this.#e = t || {};
1387
+ }
1388
+ /**
1389
+ * Processes an incoming event and triggers window actions.
1390
+ *
1391
+ * @param {string} eventName - The event identifier (e.g., "user:updated").
1392
+ * @param {Object} payload - Data associated with the event (must contain id for param matching).
1393
+ */
1394
+ handleEvent(t, e) {
1395
+ if (!this._provider) return;
1396
+ const [s, i] = t.split(":"), o = i === "deleted", r = e?.id || null, n = this.#e[t] || [];
1397
+ this._provider.getWindows().forEach(([c, d]) => {
1398
+ const u = W.normalize(c);
1399
+ if (o && r && d.dataset.dependsOn && this._shouldCloseByDependency(d.dataset.dependsOn, s, r)) {
1400
+ setTimeout(() => this._provider.close(d), this.#t);
1401
+ return;
1402
+ }
1403
+ n.some(
1404
+ (p) => W.match(p, u, o ? null : r)
1405
+ ) && setTimeout(() => {
1406
+ this._provider.open(c, !0, null, !1);
1407
+ }, this.#t);
1408
+ });
1409
+ }
1410
+ /**
1411
+ * Checks if a window should be closed based on its data-depends-on attribute.
1412
+ * Format: "category:id|category:id"
1413
+ *
1414
+ * @private
1415
+ */
1416
+ _shouldCloseByDependency(t, e, s) {
1417
+ return t.split("|").some((o) => {
1418
+ const [r, n] = o.split(":");
1419
+ return e === r && String(n) === String(s);
1420
+ });
1421
+ }
1422
+ }
1423
+ class it extends R {
1424
+ _windowManager = null;
1425
+ _toastOffsetCssVars = {
1426
+ top: "--nd-toast-top-offset",
1427
+ right: "--nd-toast-right-offset",
1428
+ bottom: "--nd-toast-bottom-offset",
1429
+ left: "--nd-toast-left-offset"
1430
+ };
1431
+ _toastOffsetObserver = null;
1432
+ _windowResizeHandler = null;
1433
+ constructor(t, e, s = {}) {
1434
+ super(t, e), this._windowManager = s.windowManager || null, this._syncWithWindowManager(), this._syncToastViewportOffsets(), this._observeToastViewportOffsets();
1435
+ }
1436
+ _syncWithWindowManager() {
1437
+ !this._windowManager || !this._windowManager._windows || this._windowManager._windows.forEach((t, e) => {
1438
+ this._updateIconState(e, !0);
1439
+ });
1440
+ }
1441
+ _bindEvents() {
1442
+ this._on("click", "[nd-taskbar-icon]", this._handleIconClick.bind(this)), document.addEventListener("window:opened", (t) => {
1443
+ const e = (
1444
+ /** @type {CustomEvent<{ endpoint: string }>} */
1445
+ t
1446
+ );
1447
+ this._updateIconState(e.detail.endpoint, !0);
1448
+ }), document.addEventListener("window:closed", (t) => {
1449
+ const e = (
1450
+ /** @type {CustomEvent<{ endpoint: string }>} */
1451
+ t
1452
+ );
1453
+ this._updateIconState(e.detail.endpoint, !1);
1454
+ });
1455
+ }
1456
+ _handleIconClick(t, e) {
1457
+ const s = e.dataset.modal;
1458
+ s && this._windowManager && this._windowManager.open(s).catch((i) => {
1459
+ i?.message !== "MAX_WINDOWS_REACHED" && console.error("Taskbar icon trigger failed:", i);
1460
+ });
1461
+ }
1462
+ _observeToastViewportOffsets() {
1463
+ this._windowResizeHandler = () => this._syncToastViewportOffsets(), window.addEventListener("resize", this._windowResizeHandler), !(typeof ResizeObserver > "u") && (this._toastOffsetObserver = new ResizeObserver(
1464
+ () => this._syncToastViewportOffsets()
1465
+ ), this._toastOffsetObserver.observe(this._root));
1466
+ }
1467
+ _setToastViewportOffsets(t) {
1468
+ const e = document?.documentElement?.style;
1469
+ e && (e.setProperty(this._toastOffsetCssVars.top, `${t.top}px`), e.setProperty(this._toastOffsetCssVars.right, `${t.right}px`), e.setProperty(this._toastOffsetCssVars.bottom, `${t.bottom}px`), e.setProperty(this._toastOffsetCssVars.left, `${t.left}px`));
1470
+ }
1471
+ _syncToastViewportOffsets() {
1472
+ const t = {
1473
+ top: 0,
1474
+ right: 0,
1475
+ bottom: 0,
1476
+ left: 0
1477
+ }, e = this._root.getBoundingClientRect();
1478
+ if (!e) {
1479
+ this._setToastViewportOffsets(t);
1480
+ return;
1481
+ }
1482
+ const s = this._root.getAttribute("nd-taskbar") === "extend", i = this._root.getAttribute("nd-taskbar-position") || "bottom", o = window.innerWidth || document.documentElement.clientWidth, r = 2;
1483
+ s || i === "bottom" ? t.bottom = Math.ceil(e.height || 0) : i === "left" && e.left <= r ? t.left = Math.ceil(e.width || 0) : i === "right" && e.right >= o - r && (t.right = Math.ceil(e.width || 0)), this._setToastViewportOffsets(t);
1484
+ }
1485
+ /**
1486
+ * Update the visual state of a taskbar icon
1487
+ * @param {string} endpoint - The window endpoint
1488
+ * @param {boolean} isOpen - Whether the window is open
1489
+ */
1490
+ _updateIconState(t, e) {
1491
+ this._queryAll(`[nd-taskbar-icon][data-modal="${t}"]`).forEach((i) => {
1492
+ e ? i.classList.add("is-open") : i.classList.remove("is-open");
1493
+ });
1494
+ }
1495
+ }
1496
+ class ot {
1497
+ #e;
1498
+ #t = /* @__PURE__ */ new Map();
1499
+ #s = null;
1500
+ /**
1501
+ * @param {import('../nidam.config.js').NidamConfig | string} config - The app configuration object or JSON string.
1502
+ */
1503
+ constructor(t = {}) {
1504
+ const e = this._parseConfig(t);
1505
+ this.#e = {
1506
+ ...P,
1507
+ ...e
1508
+ };
1509
+ }
1510
+ initialize() {
1511
+ return this.#i(), this.#o(), this.#a(), this.#n(), this;
1512
+ }
1513
+ getModule(t) {
1514
+ return this.#t.get(t);
1515
+ }
1516
+ getModules() {
1517
+ return this.#t;
1518
+ }
1519
+ #i() {
1520
+ this.#s = new k(this.#e.root), this.#t.set("delegator", this.#s);
1521
+ }
1522
+ #o() {
1523
+ const t = this.#e.root.querySelector(
1524
+ this.#e.modalContainer
1525
+ );
1526
+ if (!t)
1527
+ return;
1528
+ const e = new et(t, this.#s, {
1529
+ getModules: () => this.#t,
1530
+ initializeContent: (o, r) => I.initialize(
1531
+ r.delegator,
1532
+ o,
1533
+ r.modules,
1534
+ this.#e.registry
1535
+ ),
1536
+ config: this.#e.windowManager || {}
1537
+ });
1538
+ this.#t.set("window", e), this.#r(t, e);
1539
+ const s = this.#e.root.querySelector("[nd-taskbar]");
1540
+ if (s) {
1541
+ const o = new it(
1542
+ s,
1543
+ this.#s,
1544
+ {
1545
+ windowManager: e
1546
+ }
1547
+ );
1548
+ this.#t.set("taskbar", o);
1549
+ }
1550
+ const i = new st(e, {
1551
+ refreshMap: this.#e.refreshMap,
1552
+ refreshTimeout: this.#e.refreshTimeout
1553
+ });
1554
+ this.#t.set("refresher", i);
1555
+ }
1556
+ #n() {
1557
+ const t = this.#e.root.querySelector("[nd-icons]");
1558
+ if (!t)
1559
+ return;
1560
+ const e = new V(t, this.#s);
1561
+ this.#t.set("icon", e);
1562
+ }
1563
+ #r(t, e) {
1564
+ const s = this.#e.pendingModalDatasetKey, i = (t?.dataset?.[s] || "").trim();
1565
+ if (!i)
1566
+ return;
1567
+ const o = i.startsWith("/") ? i.slice(1) : i;
1568
+ e.open(o);
1569
+ }
1570
+ #a() {
1571
+ I.initialize(
1572
+ this.#s,
1573
+ this.#e.root,
1574
+ this.#t,
1575
+ this.#e.registry
1576
+ );
1577
+ }
1578
+ _parseConfig(t) {
1579
+ if (typeof t == "string")
1580
+ try {
1581
+ return JSON.parse(t);
1582
+ } catch {
1583
+ return T(
1584
+ "error",
1585
+ "Parsing error, falling back to default settings."
1586
+ ), {};
1587
+ }
1588
+ return t || {};
1589
+ }
1590
+ }
1591
+ const nt = (a = {}) => new ot(a);
1592
+ let v = null;
1593
+ function rt(a = {}) {
1594
+ return v ? (console.warn("[nidamjs] App is already initialized."), v) : (v = nt(a), v.initialize());
1595
+ }
1596
+ function at() {
1597
+ if (typeof document > "u")
1598
+ return;
1599
+ const a = document.querySelector("script[data-nd-init]");
1600
+ !v && !a && rt();
1070
1601
  }
1071
- function A(r, t) {
1072
- if (!r || !t || t.type !== "success" || !t.emit)
1602
+ function ct(a, t) {
1603
+ if (!a || !t || t.type !== "success" || !t.emit)
1073
1604
  return;
1074
- let s = t.emit, e = t.id || null;
1075
- const i = s.split(":");
1076
- i.length >= 3 && (e = i.pop(), s = i.join(":")), r.handleEvent(s, { ...t, emit: s, id: e });
1605
+ let e = t.emit, s = t.id || null;
1606
+ const i = e.split(":");
1607
+ i.length >= 3 && (s = i.pop(), e = i.join(":")), a.handleEvent(e, { ...t, emit: e, id: s });
1077
1608
  }
1078
- const N = $();
1079
- N.initialize();
1609
+ at();
1080
1610
  export {
1081
- z as BaseManager,
1082
- x as ContentInitializer,
1083
- R as DOMUtils,
1084
- X as DesktopIconManager,
1085
- b as EventDelegator,
1086
- P as NidamApp,
1087
- D as WindowManager,
1088
- I as WindowRefresher,
1089
- L as applyWindowState,
1090
- T as captureWindowState,
1091
- $ as createNidamApp,
1092
- A as handleRefreshEvent,
1093
- v as readWindowState,
1094
- C as saveWindowState,
1095
- M as storageUtil
1611
+ R as BaseManager,
1612
+ I as ContentInitializer,
1613
+ q as DOMUtils,
1614
+ k as EventDelegator,
1615
+ V as IconManager,
1616
+ ot as NidamApp,
1617
+ et as WindowManager,
1618
+ st as WindowRefresher,
1619
+ l as WindowState,
1620
+ nt as createNidamApp,
1621
+ tt as createToastNotify,
1622
+ rt as default,
1623
+ ct as handleRefreshEvent,
1624
+ rt as initNidamApp,
1625
+ Q as showToast,
1626
+ O as storageUtil,
1627
+ T as toastNotify
1096
1628
  };