@necrolab/dashboard 0.4.61 → 0.4.208

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.
Files changed (133) hide show
  1. package/.prettierrc +1 -27
  2. package/.vscode/extensions.json +1 -1
  3. package/README.md +79 -43
  4. package/backend/api.js +48 -40
  5. package/backend/auth.js +3 -3
  6. package/backend/batching.js +1 -1
  7. package/backend/endpoints.js +77 -13
  8. package/backend/index.js +2 -2
  9. package/backend/mock-data.js +38 -29
  10. package/backend/mock-src/classes/logger.js +8 -8
  11. package/backend/mock-src/classes/utils.js +3 -7
  12. package/backend/mock-src/database.js +0 -0
  13. package/backend/mock-src/ticketmaster.js +79 -79
  14. package/backend/validator.js +2 -2
  15. package/config/configs.json +3 -2
  16. package/config/filter.json +3 -2
  17. package/index.html +10 -81
  18. package/index.js +1 -1
  19. package/package.json +25 -40
  20. package/postcss.config.js +1 -1
  21. package/postinstall.js +17 -98
  22. package/public/android-chrome-192x192.png +0 -0
  23. package/public/android-chrome-512x512.png +0 -0
  24. package/public/apple-touch-icon.png +0 -0
  25. package/public/favicon-16x16.png +0 -0
  26. package/public/favicon-32x32.png +0 -0
  27. package/public/favicon.ico +0 -0
  28. package/public/manifest.json +7 -12
  29. package/public/sw.js +2 -0
  30. package/public/workbox-49fdaf31.js +2 -0
  31. package/public/workbox-49fdaf31.js.map +1 -0
  32. package/public/workbox-88575b92.js +2 -0
  33. package/public/workbox-88575b92.js.map +1 -0
  34. package/public/workbox-a67a7b11.js +2 -0
  35. package/public/workbox-a67a7b11.js.map +1 -0
  36. package/public/workbox-d4314735.js +2 -0
  37. package/public/workbox-d4314735.js.map +1 -0
  38. package/public/workbox-e0f89ef3.js +2 -0
  39. package/public/workbox-e0f89ef3.js.map +1 -0
  40. package/run +9 -176
  41. package/src/App.vue +85 -498
  42. package/src/assets/css/_input.scss +99 -144
  43. package/src/assets/css/main.scss +99 -450
  44. package/src/assets/img/background.svg +2 -2
  45. package/src/assets/img/logo_icon.png +0 -0
  46. package/src/components/Auth/LoginForm.vue +11 -62
  47. package/src/components/Editors/Account/Account.vue +40 -116
  48. package/src/components/Editors/Account/AccountCreator.vue +39 -88
  49. package/src/components/Editors/Account/AccountView.vue +34 -102
  50. package/src/components/Editors/Account/CreateAccount.vue +32 -80
  51. package/src/components/Editors/Profile/CreateProfile.vue +83 -269
  52. package/src/components/Editors/Profile/Profile.vue +47 -132
  53. package/src/components/Editors/Profile/ProfileCountryChooser.vue +20 -82
  54. package/src/components/Editors/Profile/ProfileView.vue +34 -91
  55. package/src/components/Editors/TagLabel.vue +6 -67
  56. package/src/components/Filter/Filter.vue +72 -289
  57. package/src/components/Filter/FilterPreview.vue +30 -171
  58. package/src/components/Filter/PriceSortToggle.vue +4 -74
  59. package/src/components/Table/Header.vue +1 -1
  60. package/src/components/Table/Row.vue +1 -1
  61. package/src/components/Table/Table.vue +2 -19
  62. package/src/components/Tasks/CheckStock.vue +13 -28
  63. package/src/components/Tasks/Controls/DesktopControls.vue +17 -17
  64. package/src/components/Tasks/Controls/MobileControls.vue +45 -8
  65. package/src/components/Tasks/CreateTaskAXS.vue +73 -79
  66. package/src/components/Tasks/CreateTaskTM.vue +142 -94
  67. package/src/components/Tasks/MassEdit.vue +7 -9
  68. package/src/components/Tasks/QuickSettings.vue +55 -169
  69. package/src/components/Tasks/ScrapeVenue.vue +4 -7
  70. package/src/components/Tasks/Stats.vue +23 -52
  71. package/src/components/Tasks/Task.vue +136 -378
  72. package/src/components/Tasks/TaskView.vue +47 -107
  73. package/src/components/Tasks/Utilities.vue +6 -5
  74. package/src/components/icons/Bag.vue +1 -1
  75. package/src/components/icons/Loyalty.vue +1 -1
  76. package/src/components/icons/Mail.vue +2 -2
  77. package/src/components/icons/Play.vue +2 -2
  78. package/src/components/icons/Reload.vue +5 -4
  79. package/src/components/icons/Sandclock.vue +2 -2
  80. package/src/components/icons/Stadium.vue +1 -1
  81. package/src/components/icons/index.js +1 -24
  82. package/src/components/ui/Modal.vue +13 -105
  83. package/src/components/ui/Navbar.vue +38 -171
  84. package/src/components/ui/ReconnectIndicator.vue +55 -351
  85. package/src/components/ui/Splash.vue +35 -5
  86. package/src/components/ui/controls/CountryChooser.vue +62 -200
  87. package/src/components/ui/controls/atomic/Checkbox.vue +10 -119
  88. package/src/components/ui/controls/atomic/Dropdown.vue +39 -208
  89. package/src/components/ui/controls/atomic/MultiDropdown.vue +37 -300
  90. package/src/libs/Filter.js +170 -200
  91. package/src/registerServiceWorker.js +1 -1
  92. package/src/stores/connection.js +53 -51
  93. package/src/stores/logger.js +3 -11
  94. package/src/stores/sampleData.js +235 -207
  95. package/src/stores/ui.js +44 -112
  96. package/src/stores/utils.js +6 -90
  97. package/src/views/Accounts.vue +35 -44
  98. package/src/views/Console.vue +90 -341
  99. package/src/views/Editor.vue +123 -1176
  100. package/src/views/FilterBuilder.vue +251 -607
  101. package/src/views/Login.vue +14 -76
  102. package/src/views/Profiles.vue +25 -44
  103. package/src/views/Tasks.vue +100 -187
  104. package/static/offline.html +50 -192
  105. package/tailwind.config.js +26 -104
  106. package/vite.config.js +16 -73
  107. package/vue.config.js +32 -0
  108. package/workbox-config.js +11 -0
  109. package/artwork/image.png +0 -0
  110. package/dev-server.js +0 -136
  111. package/exit +0 -209
  112. package/jsconfig.json +0 -16
  113. package/src/assets/css/_utilities.scss +0 -388
  114. package/src/assets/img/background.svg.backup +0 -11
  115. package/src/components/icons/Check.vue +0 -5
  116. package/src/components/icons/Close.vue +0 -21
  117. package/src/components/icons/CloseX.vue +0 -5
  118. package/src/components/icons/Key.vue +0 -21
  119. package/src/components/icons/Pencil.vue +0 -21
  120. package/src/components/icons/Profile.vue +0 -18
  121. package/src/components/icons/Sell.vue +0 -21
  122. package/src/components/icons/Spinner.vue +0 -42
  123. package/src/components/icons/SquareCheck.vue +0 -18
  124. package/src/components/icons/SquareUncheck.vue +0 -18
  125. package/src/components/icons/Wildcard.vue +0 -18
  126. package/src/components/ui/controls/atomic/LoadingButton.vue +0 -45
  127. package/src/composables/useClickOutside.js +0 -21
  128. package/src/composables/useDropdownPosition.js +0 -174
  129. package/src/types/index.js +0 -41
  130. package/src/utils/debug.js +0 -1
  131. package/switch-branch.sh +0 -41
  132. package/workbox-config.cjs +0 -63
  133. /package/src/assets/img/{logo_icon-old.png → logo_icon_2.png} +0 -0
package/src/App.vue CHANGED
@@ -9,23 +9,27 @@
9
9
  :style="{
10
10
  'margin-top': maxPull() + 'px',
11
11
  transform: `rotate(${ui.pullChange}deg)`
12
- }">
12
+ }"
13
+ >
13
14
  <div
14
15
  class="refresh-icon p-2 rounded-full mx-auto duration-200"
15
16
  :class="{
16
17
  'opacity-100': ui.pullChange > 250,
17
18
  'opacity-0': ui.pullChange < 250
18
- }">
19
+ }"
20
+ >
19
21
  <svg
20
22
  xmlns="http://www.w3.org/2000/svg"
21
23
  fill="none"
22
24
  viewBox="0 0 24 24"
23
25
  class="stroke-2 w-4 stroke-dark-400"
24
- :class="{ 'opacity-0': ui.pullChange == 0 }">
26
+ :class="{ 'opacity-0': ui.pullChange == 0 }"
27
+ >
25
28
  <path
26
29
  strokeLinecap="round"
27
30
  strokeLinejoin="round"
28
- d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" />
31
+ d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
32
+ />
29
33
  </svg>
30
34
  </div>
31
35
  </div>
@@ -34,7 +38,8 @@
34
38
  :class="{
35
39
  'opacity-100': ui.pullChange > 250,
36
40
  'opacity-0': ui.pullChange < 250
37
- }">
41
+ }"
42
+ >
38
43
  Release to refresh
39
44
  </h2>
40
45
  </div>
@@ -44,17 +49,12 @@
44
49
  </div>
45
50
  <div v-else key="main-components" class="flex">
46
51
  <Navbar v-if="layout == 'dashboard'" class="fixed" />
47
- <div :class="['router-wrapper', { '-mt-[60px]': ui.pullChange > 1 }]"></div>
48
- <router-view v-slot="{ Component, route }">
49
- <transition name="page-transition" mode="out-in">
50
- <component
51
- :is="Component"
52
- :key="route.path"
53
- :class="[
54
- 'component-container pb-2 mt-0 lg:mt-4 ios-wrapper',
55
- { 'w-full': landscapeIos }
56
- ]" />
57
- </transition>
52
+ <div :class="`router-wrapper ${ui.pullChange > 1 ? '-mt-[60px]' : ''}`"></div>
53
+ <router-view v-slot="{ Component }">
54
+ <component
55
+ :is="Component"
56
+ :class="`component-container pb-2 mt-0 lg:mt-4 ios-wrapper ${landscapeIos ? 'w-full' : ''}`"
57
+ />
58
58
  </router-view>
59
59
  </div>
60
60
  </transition>
@@ -63,7 +63,7 @@
63
63
 
64
64
  <script setup>
65
65
  import { storeToRefs } from "pinia";
66
- import { ref, computed, watch, onMounted } from "vue";
66
+ import { ref, computed, watch } from "vue";
67
67
  import { useRouter } from "vue-router";
68
68
  import Navbar from "@/components/ui/Navbar.vue";
69
69
  import { useUIStore } from "@/stores/ui";
@@ -88,50 +88,13 @@ function isIpadOS() {
88
88
  return navigator.maxTouchPoints && navigator.maxTouchPoints > 2 && /MacIntel/.test(navigator.platform);
89
89
  }
90
90
 
91
- // Handle orientation changes for iOS devices
92
- const handleOrientationChange = () => {
93
- if (isIOS()) {
94
- if (isIpadOS()) {
95
- // For iPad, we want to allow proper viewport resizing
96
- landscapeIos.value = window.matchMedia("(orientation: landscape)").matches;
97
- document.documentElement.style.setProperty("width", "100%", "important");
98
- document.body.style.setProperty("width", "100%", "important");
99
- } else {
100
- // For iPhone, maintain the current behavior
101
- landscapeIos.value = window.matchMedia("(orientation: landscape)").matches;
102
- }
103
- }
104
- };
105
-
106
- // Initial orientation check
107
- handleOrientationChange();
108
-
109
- // Listen for orientation changes
110
- window.matchMedia("(orientation: portrait)").addEventListener("change", handleOrientationChange);
111
- window.matchMedia("(orientation: landscape)").addEventListener("change", handleOrientationChange);
91
+ window.matchMedia("(orientation: portrait)").addEventListener("change", (e) => {
92
+ if (!e.matches && isIOS() && !isIpadOS()) landscapeIos.value = true;
93
+ else landscapeIos.value = false;
94
+ });
112
95
 
113
96
  if (!window.location.href.includes(":5173")) ui.startSpinner("Loading...");
114
97
 
115
- // Ensure notch handling runs when Vue app mounts
116
- onMounted(() => {
117
- // Multiple aggressive attempts on Vue mount
118
- handleNotch();
119
- setTimeout(handleNotch, 10);
120
- setTimeout(handleNotch, 50);
121
- setTimeout(handleNotch, 150);
122
- setTimeout(handleNotch, 300);
123
-
124
- // Set up a recurring check for the first few seconds
125
- let attempts = 0;
126
- const recurringCheck = setInterval(() => {
127
- if (attempts++ > 10) {
128
- clearInterval(recurringCheck);
129
- return;
130
- }
131
- handleNotch();
132
- }, 200);
133
- });
134
-
135
98
  // close modals on esc
136
99
  document.onkeydown = function (evt) {
137
100
  evt = evt || window.event;
@@ -159,64 +122,13 @@ document.addEventListener("keydown", function (event) {
159
122
  event.preventDefault();
160
123
  }
161
124
  });
162
- // Nuclear iOS keyboard prevention - lock everything down
163
- let isIOSDevice =
164
- /iPad|iPhone|iPod/.test(navigator.platform) ||
165
- (navigator.maxTouchPoints && navigator.maxTouchPoints > 2 && /MacIntel/.test(navigator.platform));
166
-
167
- if (isIOSDevice) {
168
- const handleViewportResize = () => {
169
- if (isIpadOS()) {
170
- // For iPad, allow natural viewport behavior
171
- return;
172
- }
173
-
174
- // For iPhone, maintain viewport stability
175
- const vh = window.innerHeight * 0.01;
176
- document.documentElement.style.setProperty("--vh", `${vh}px`);
177
- };
178
-
179
- // Initial setup
180
- handleViewportResize();
181
-
182
- // Listen for viewport changes
183
- window.addEventListener("resize", handleViewportResize);
184
- if (window.visualViewport) {
185
- window.visualViewport.addEventListener("resize", handleViewportResize);
186
- }
187
- }
188
-
189
- // Precise mouse wheel control - only allow scrolling within specific scrollable elements
125
+ // prevent zoom
190
126
  window.addEventListener(
191
127
  "mousewheel",
192
128
  function (event) {
193
- // Check if we're on a scrollable textarea
194
- const isScrollableTextarea =
195
- event.target.tagName === "TEXTAREA" &&
196
- (event.target.classList.contains("code-editor") || event.target.classList.contains("proxy-editor"));
197
-
198
- // Check if we're in a table but only allow scrolling on actual scrollable content
199
- const isInTable = event.target.closest(".table-component");
200
- const isScrollableTableContent =
201
- isInTable &&
202
- (event.target.closest(".grid") || // Table rows
203
- event.target.closest(".table-row") ||
204
- (event.target.closest(".table-component") && !event.target.closest(".table-header")));
205
-
206
- // Allow scrolling in navbar and modals
207
- const isInNavbar = event.target.closest(".navbar") || event.target.closest(".mobile-menu");
208
- const isInModal = event.target.closest('[role="dialog"]');
209
-
210
- // Only allow these specific cases
211
- if (isScrollableTextarea || isScrollableTableContent || isInNavbar || isInModal) {
212
- // Stop propagation to prevent page scroll
213
- if (isScrollableTableContent || isScrollableTextarea) {
214
- event.stopPropagation();
215
- }
216
- return;
129
+ if (event.ctrlKey == true) {
130
+ event.preventDefault();
217
131
  }
218
-
219
- event.preventDefault();
220
132
  },
221
133
  { passive: false }
222
134
  );
@@ -224,349 +136,69 @@ window.addEventListener(
224
136
  window.addEventListener(
225
137
  "DOMMouseScroll",
226
138
  function (event) {
227
- // Use same logic as mousewheel
228
- const isScrollableTextarea =
229
- event.target.tagName === "TEXTAREA" &&
230
- (event.target.classList.contains("code-editor") || event.target.classList.contains("proxy-editor"));
231
-
232
- const isInTable = event.target.closest(".table-component");
233
- const isScrollableTableContent =
234
- isInTable &&
235
- (event.target.closest(".grid") ||
236
- event.target.closest(".table-row") ||
237
- (event.target.closest(".table-component") && !event.target.closest(".table-header")));
238
-
239
- const isInNavbar = event.target.closest(".navbar") || event.target.closest(".mobile-menu");
240
- const isInModal = event.target.closest('[role="dialog"]');
241
-
242
- if (isScrollableTextarea || isScrollableTableContent || isInNavbar || isInModal) {
243
- if (isScrollableTableContent || isScrollableTextarea) {
244
- event.stopPropagation();
245
- }
246
- return;
247
- }
248
-
249
- event.preventDefault();
250
- },
251
- { passive: false }
252
- );
253
-
254
- window.addEventListener(
255
- "wheel",
256
- function (event) {
257
- // Use same logic as mousewheel
258
- const isScrollableTextarea =
259
- event.target.tagName === "TEXTAREA" &&
260
- (event.target.classList.contains("code-editor") || event.target.classList.contains("proxy-editor"));
261
-
262
- const isInTable = event.target.closest(".table-component");
263
- const isScrollableTableContent =
264
- isInTable &&
265
- (event.target.closest(".grid") ||
266
- event.target.closest(".table-row") ||
267
- (event.target.closest(".table-component") && !event.target.closest(".table-header")));
268
-
269
- const isInNavbar = event.target.closest(".navbar") || event.target.closest(".mobile-menu");
270
- const isInModal = event.target.closest('[role="dialog"]');
271
-
272
- if (isScrollableTextarea || isScrollableTableContent || isInNavbar || isInModal) {
273
- if (isScrollableTableContent || isScrollableTextarea) {
274
- event.stopPropagation();
275
- }
276
- return;
277
- }
278
-
279
- event.preventDefault();
280
- },
281
- { passive: false }
282
- );
283
-
284
- window.addEventListener(
285
- "scroll",
286
- function (event) {
287
- event.preventDefault();
288
- },
289
- { passive: false }
290
- );
291
-
292
- // Precise scroll control - only allow scrolling within specific scrollable elements
293
- window.addEventListener(
294
- "touchmove",
295
- function (event) {
296
- // Check if we're touching a scrollable element directly
297
- const isScrollableTextarea =
298
- event.target.tagName === "TEXTAREA" &&
299
- (event.target.classList.contains("code-editor") || event.target.classList.contains("proxy-editor"));
300
-
301
- // Check if we're in a table but only allow scrolling on actual scrollable content
302
- const isInTable = event.target.closest(".table-component");
303
- const isScrollableTableContent =
304
- isInTable &&
305
- (event.target.closest(".grid") || // Table rows
306
- event.target.closest(".table-row") ||
307
- (event.target.closest(".table-component") && !event.target.closest(".table-header")));
308
-
309
- // Allow scrolling in navbar and modals (they handle their own boundaries)
310
- const isInNavbar = event.target.closest(".navbar") || event.target.closest(".mobile-menu");
311
- const isInModal = event.target.closest('[role="dialog"]');
312
-
313
- // Only allow these specific cases
314
- if (isScrollableTextarea || isScrollableTableContent || isInNavbar || isInModal) {
315
- // For table content, ensure we stop propagation to prevent page scroll
316
- if (isScrollableTableContent || isScrollableTextarea) {
317
- event.stopPropagation();
318
- }
319
- return;
139
+ if (event.ctrlKey == true) {
140
+ event.preventDefault();
320
141
  }
321
-
322
- // Prevent everything else
323
- event.preventDefault();
324
142
  },
325
143
  { passive: false }
326
144
  );
327
145
 
328
- // PERFECT notch handling - simple and bulletproof
329
- let notchTimeout;
330
- let isNotchBusy = false;
331
- let lastNotchState = null;
332
- let animationTimeout;
333
- let pendingUpdate = false;
334
-
335
- function isLandscapeMode() {
336
- // Check orientation first
337
- let orientationLandscape = false;
338
-
339
- if (typeof window.orientation !== "undefined") {
340
- orientationLandscape = Math.abs(window.orientation) === 90;
341
- } else if (screen.orientation && typeof screen.orientation.angle !== "undefined") {
342
- orientationLandscape = Math.abs(screen.orientation.angle) === 90;
146
+ window.screen.orientation.onchange = () => {
147
+ if (document.body.classList.contains("overflow-hidden")) {
148
+ document.body.classList.remove("overflow-hidden");
149
+ setTimeout(() => {
150
+ document.body.classList.add("overflow-hidden");
151
+ }, 5);
343
152
  }
153
+ handleNotch();
154
+ };
344
155
 
345
- // Always also check dimensions as backup
346
- const dimensionLandscape = window.innerWidth > window.innerHeight;
347
- const hasStrongLandscapeRatio = window.innerWidth / window.innerHeight > 1.5;
348
-
349
- // For iPhones, if dimensions clearly indicate landscape, trust that
350
- if (isIOS() && !isIpadOS() && dimensionLandscape && hasStrongLandscapeRatio) {
351
- return true;
352
- }
353
-
354
- // Otherwise use orientation if available, fallback to dimensions
355
- return orientationLandscape || (dimensionLandscape && hasStrongLandscapeRatio);
356
- }
357
-
358
- function hasDeviceNotch() {
359
- // Only check for notch on actual devices that might have one
360
- if (!isIOS() || isIpadOS()) return false;
361
-
362
- return (
363
- CSS.supports("padding-left: env(safe-area-inset-left)") &&
364
- CSS.supports("padding-right: env(safe-area-inset-right)")
365
- );
366
- }
367
-
368
- function handleNotch(force = false) {
369
- // Simple debouncing
370
- if (isNotchBusy && !force) return;
371
-
372
- try {
373
- // Only for iPhone (not iPad)
374
- if (!isIOS() || isIpadOS()) return;
375
-
376
- const wrappers = document.querySelectorAll(".ios-wrapper");
377
- if (wrappers.length === 0) {
378
- setTimeout(() => handleNotch(force), 100);
379
- return;
380
- }
381
-
382
- const isLandscape = isLandscapeMode();
383
- const hasNotch = hasDeviceNotch();
384
-
385
- // Get orientation
386
- let orientation = window.orientation;
387
- if (typeof orientation === "undefined" && screen.orientation) {
388
- orientation = screen.orientation.angle;
389
- }
390
-
391
- // Create state signature to prevent redundant updates
392
- const currentState = `${isLandscape}-${hasNotch}-${orientation}-${window.innerWidth}x${window.innerHeight}`;
393
- if (lastNotchState === currentState && !force) {
394
- return;
395
- }
396
- lastNotchState = currentState;
397
-
398
- isNotchBusy = true;
399
-
400
- // Debug info
401
- if (window.location.href.includes("localhost") || window.location.href.includes("5173")) {
402
- console.log("🔥 Notch Debug:", {
403
- isLandscape,
404
- hasNotch,
405
- orientation,
406
- dimensions: `${window.innerWidth}x${window.innerHeight}`,
407
- state: currentState
408
- });
409
- }
410
-
411
- if (hasNotch && isLandscape) {
412
- // Test actual safe area values to determine notch side
413
- const testDiv = document.createElement("div");
414
- testDiv.style.position = "fixed";
415
- testDiv.style.top = "0";
416
- testDiv.style.left = "0";
417
- testDiv.style.visibility = "hidden";
418
- testDiv.style.paddingLeft = "env(safe-area-inset-left)";
419
- testDiv.style.paddingRight = "env(safe-area-inset-right)";
420
- document.body.appendChild(testDiv);
421
-
422
- const computedStyle = getComputedStyle(testDiv);
423
- const leftInset = parseFloat(computedStyle.paddingLeft) || 0;
424
- const rightInset = parseFloat(computedStyle.paddingRight) || 0;
156
+ document.addEventListener("DOMContentLoaded", handleNotch, false);
157
+ window.simulateRotate = handleNotch;
425
158
 
426
- document.body.removeChild(testDiv);
159
+ function handleNotch() {
160
+ const wrappers = [...document.querySelectorAll(".ios-wrapper")];
427
161
 
428
- console.log("🔍 Safe area insets:", { leftInset, rightInset });
162
+ // Check if the device is an iPhone with a notch
163
+ const hasNotch = CSS.supports("padding-left: env(safe-area-inset-left)");
429
164
 
430
- // Apply styles instantly - NO ANIMATION
431
- wrappers.forEach((wrapper) => {
432
- if (leftInset > 0) {
433
- // Notch on left
165
+ const orientation = window.orientation; //window.screen.orientation.angle;
166
+ if (!isIpadOS() && isIOS()) {
167
+ if (hasNotch && orientation !== 0) {
168
+ // Landscape mode
169
+ if (orientation === 90) {
170
+ // Notch is on the left
171
+ wrappers.forEach((wrapper) => {
434
172
  wrapper.style.paddingLeft = "env(safe-area-inset-left)";
435
173
  wrapper.style.paddingRight = "0.5rem";
436
- } else if (rightInset > 0) {
437
- // Notch on right
438
- wrapper.style.paddingLeft = "0.5rem";
174
+ });
175
+ } else if (orientation === -90) {
176
+ // Notch is on the right
177
+ wrappers.forEach((wrapper) => {
439
178
  wrapper.style.paddingRight = "env(safe-area-inset-right)";
440
- } else {
441
- // Fallback - no detectable notch
442
179
  wrapper.style.paddingLeft = "0.5rem";
443
- wrapper.style.paddingRight = "0.5rem";
444
- }
445
- });
180
+ });
181
+ }
446
182
  } else {
447
- // Portrait or no notch
448
- const padding =
449
- window.innerWidth > 1280
450
- ? "2.5rem"
451
- : window.innerWidth > 1030
452
- ? "1.5rem"
453
- : window.innerWidth > 768
454
- ? "0.5rem"
455
- : "0.5rem";
456
-
457
- // Apply styles instantly - NO ANIMATION
458
183
  wrappers.forEach((wrapper) => {
459
- wrapper.style.paddingLeft = padding;
460
- wrapper.style.paddingRight = padding;
184
+ // Portrait mode or no notch
185
+ const amount =
186
+ window.innerWidth > 1280
187
+ ? "2.5rem"
188
+ : window.innerWidth > 1030
189
+ ? "1.5rem"
190
+ : window.innerWidth > 768
191
+ ? "0.5rem"
192
+ : "0.5rem";
193
+ wrapper.style.paddingLeft = amount;
194
+ wrapper.style.paddingRight = amount;
461
195
  });
462
196
  }
463
-
464
- isNotchBusy = false;
465
- } catch (error) {
466
- console.warn("Notch error:", error);
467
- isNotchBusy = false;
468
197
  }
469
198
  }
470
199
 
471
- function triggerNotch() {
472
- clearTimeout(notchTimeout);
473
- notchTimeout = setTimeout(handleNotch, 50);
474
- }
475
-
476
- // Event listeners
477
- window.addEventListener("orientationchange", triggerNotch);
478
- window.addEventListener("resize", triggerNotch);
479
- if (screen.orientation) {
480
- screen.orientation.addEventListener("change", triggerNotch);
481
- }
482
-
483
- // Aggressive initial setup to handle all scenarios
484
- function initNotchHandling() {
485
- handleNotch(true); // Force first execution
486
- setTimeout(() => handleNotch(true), 50);
487
- setTimeout(() => handleNotch(true), 150);
488
- setTimeout(() => handleNotch(true), 300);
489
- setTimeout(() => handleNotch(true), 500);
490
- setTimeout(() => handleNotch(true), 1000);
491
- setTimeout(() => handleNotch(true), 2000); // Extra long delay for slow loads
492
- }
493
-
494
- // Multiple initialization points
495
- if (document.readyState === "loading") {
496
- document.addEventListener("DOMContentLoaded", initNotchHandling);
497
- } else {
498
- initNotchHandling();
499
- }
500
-
501
- window.addEventListener("load", initNotchHandling);
502
-
503
- // Also run when page becomes visible (handles app switching)
504
- document.addEventListener("visibilitychange", () => {
505
- if (!document.hidden) {
506
- setTimeout(handleNotch, 100);
507
- }
508
- });
509
-
510
- // Run on focus (when returning to app)
511
- window.addEventListener("focus", () => {
512
- setTimeout(handleNotch, 100);
513
- });
514
-
515
- // Watch for ios-wrapper elements appearing in DOM
516
- const observer = new MutationObserver((mutations) => {
517
- let shouldTrigger = false;
518
-
519
- mutations.forEach((mutation) => {
520
- if (mutation.type === "childList") {
521
- mutation.addedNodes.forEach((node) => {
522
- if (node.nodeType === Node.ELEMENT_NODE) {
523
- if (node.classList?.contains("ios-wrapper") || node.querySelector?.(".ios-wrapper")) {
524
- shouldTrigger = true;
525
- }
526
- }
527
- });
528
- }
529
- });
530
-
531
- if (shouldTrigger) {
532
- setTimeout(() => handleNotch(true), 10);
533
- setTimeout(() => handleNotch(true), 100);
534
- }
535
- });
536
-
537
- // Start observing
538
- if (document.body) {
539
- observer.observe(document.body, {
540
- childList: true,
541
- subtree: true
542
- });
543
- } else {
544
- document.addEventListener("DOMContentLoaded", () => {
545
- observer.observe(document.body, {
546
- childList: true,
547
- subtree: true
548
- });
549
- });
550
- }
551
-
552
- // Navigation handling
553
- const originalPushState = history.pushState;
554
- const originalReplaceState = history.replaceState;
555
-
556
- history.pushState = function (...args) {
557
- originalPushState.apply(this, args);
558
- triggerNotch();
559
- };
560
-
561
- history.replaceState = function (...args) {
562
- originalReplaceState.apply(this, args);
563
- triggerNotch();
564
- };
565
-
566
- window.addEventListener("popstate", triggerNotch);
567
-
568
- // Expose for manual triggering
569
- window.simulateRotate = handleNotch;
200
+ // Call the function initially
201
+ window.addEventListener("orientationchange", handleNotch);
570
202
 
571
203
  const pullStart = (e) => {
572
204
  const { screenY } = e.targetTouches[0];
@@ -620,57 +252,33 @@ function maxPull() {
620
252
  return 250;
621
253
  }
622
254
  }
623
- // Vue router integration - aggressive triggering on route changes
624
255
  watch(
625
256
  () => router.currentRoute.value.name,
626
257
  () => {
627
- // Immediate triggers
628
- handleNotch();
629
- triggerNotch();
630
-
631
- // Staggered attempts
632
- setTimeout(handleNotch, 10);
633
- setTimeout(handleNotch, 50);
634
- setTimeout(handleNotch, 150);
635
- setTimeout(handleNotch, 300);
636
- setTimeout(handleNotch, 500);
637
- }
638
- );
639
-
640
- watch(
641
- () => router.currentRoute.value.path,
642
- () => {
643
- // Immediate triggers
644
- handleNotch();
645
- triggerNotch();
646
-
647
- // Staggered attempts
648
- setTimeout(handleNotch, 25);
649
- setTimeout(handleNotch, 100);
650
- setTimeout(handleNotch, 250);
258
+ setTimeout(() => handleNotch(), 5);
651
259
  }
652
260
  );
653
261
  const layout = computed(() => router.currentRoute.value.meta.layout);
654
262
  </script>
655
263
  <style lang="scss">
656
- // Ultra bulletproof scroll prevention
657
- html,
658
- body {
659
- overflow: hidden !important;
660
- overscroll-behavior: none !important;
661
- width: 100% !important;
662
- height: 100% !important;
663
- touch-action: none !important;
264
+ .task-buttons {
265
+ @apply flex mx-auto gap-x-4;
266
+ svg {
267
+ width: 15px;
268
+ height: 15px;
269
+ }
664
270
  }
665
271
 
666
- #app {
667
- overflow: hidden !important;
668
- overscroll-behavior: none !important;
669
- width: 100% !important;
670
- height: 100vh !important;
671
- touch-action: none !important;
272
+ .hidden-scrollbars::-webkit-scrollbar
273
+ /* Hide scrollbar for Chrome, Safari and Opera */ {
274
+ display: none;
672
275
  }
673
276
 
277
+ /* Hide scrollbar for IE, Edge and Firefox */
278
+ .hidden-scrollbars {
279
+ -ms-overflow-style: none; /* IE and Edge */
280
+ scrollbar-width: none; /* Firefox */
281
+ }
674
282
  .dropdown {
675
283
  position: relative;
676
284
  display: inline-block;
@@ -684,19 +292,19 @@ body {
684
292
  z-index: 1;
685
293
  }
686
294
 
295
+ .smooth-hover {
296
+ @apply hover:opacity-70 active:opacity-50 duration-150;
297
+ }
298
+
687
299
  .layout {
688
300
  @apply flex flex-col;
689
301
  min-height: 90vh;
690
- overflow: hidden !important;
691
- height: 100vh !important;
692
302
  }
693
-
694
303
  .router-wrapper {
695
304
  @apply pt-5;
696
305
  transition: margin 0.25s;
697
306
  z-index: 0;
698
307
  }
699
-
700
308
  .refresh-container {
701
309
  transition: margin 0.25s;
702
310
  }
@@ -704,25 +312,4 @@ body {
704
312
  .component-container {
705
313
  @apply w-full mx-auto px-4 xs:px-4 md:px-2 lg:px-6 xl:px-10;
706
314
  }
707
-
708
- // Page navigation transitions
709
- .page-transition-enter-active {
710
- transition: all 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94);
711
- }
712
-
713
- .page-transition-leave-active {
714
- transition: all 0.15s cubic-bezier(0.55, 0.085, 0.68, 0.53);
715
- }
716
-
717
- .page-transition-enter-from {
718
- opacity: 0;
719
- transform: translateX(15px) scale(0.98);
720
- filter: blur(1px);
721
- }
722
-
723
- .page-transition-leave-to {
724
- opacity: 0;
725
- transform: translateX(-10px) scale(1.02);
726
- filter: blur(0.5px);
727
- }
728
315
  </style>