@necrolab/dashboard 0.4.50 → 0.4.52
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/package.json +2 -2
- package/src/App.vue +178 -195
- package/src/assets/css/_input.scss +2 -4
- package/src/assets/css/_utilities.scss +0 -53
- package/src/assets/css/main.scss +0 -25
- package/src/components/Editors/Account/AccountView.vue +12 -13
- package/src/components/Editors/Profile/ProfileCountryChooser.vue +4 -6
- package/src/components/Filter/FilterPreview.vue +24 -37
- package/src/components/Tasks/Task.vue +422 -487
- package/src/components/ui/Modal.vue +21 -17
- package/src/components/ui/controls/atomic/Dropdown.vue +126 -131
- package/src/components/ui/controls/atomic/MultiDropdown.vue +200 -206
- package/src/views/Console.vue +1 -1
- package/src/views/Editor.vue +952 -1037
- package/src/views/FilterBuilder.vue +1 -1
- package/src/views/Login.vue +57 -63
package/src/App.vue
CHANGED
|
@@ -9,27 +9,23 @@
|
|
|
9
9
|
:style="{
|
|
10
10
|
'margin-top': maxPull() + 'px',
|
|
11
11
|
transform: `rotate(${ui.pullChange}deg)`
|
|
12
|
-
}"
|
|
13
|
-
>
|
|
12
|
+
}">
|
|
14
13
|
<div
|
|
15
14
|
class="refresh-icon p-2 rounded-full mx-auto duration-200"
|
|
16
15
|
:class="{
|
|
17
16
|
'opacity-100': ui.pullChange > 250,
|
|
18
17
|
'opacity-0': ui.pullChange < 250
|
|
19
|
-
}"
|
|
20
|
-
>
|
|
18
|
+
}">
|
|
21
19
|
<svg
|
|
22
20
|
xmlns="http://www.w3.org/2000/svg"
|
|
23
21
|
fill="none"
|
|
24
22
|
viewBox="0 0 24 24"
|
|
25
23
|
class="stroke-2 w-4 stroke-dark-400"
|
|
26
|
-
:class="{ 'opacity-0': ui.pullChange == 0 }"
|
|
27
|
-
>
|
|
24
|
+
:class="{ 'opacity-0': ui.pullChange == 0 }">
|
|
28
25
|
<path
|
|
29
26
|
strokeLinecap="round"
|
|
30
27
|
strokeLinejoin="round"
|
|
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
|
-
/>
|
|
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" />
|
|
33
29
|
</svg>
|
|
34
30
|
</div>
|
|
35
31
|
</div>
|
|
@@ -38,8 +34,7 @@
|
|
|
38
34
|
:class="{
|
|
39
35
|
'opacity-100': ui.pullChange > 250,
|
|
40
36
|
'opacity-0': ui.pullChange < 250
|
|
41
|
-
}"
|
|
42
|
-
>
|
|
37
|
+
}">
|
|
43
38
|
Release to refresh
|
|
44
39
|
</h2>
|
|
45
40
|
</div>
|
|
@@ -58,8 +53,7 @@
|
|
|
58
53
|
:class="[
|
|
59
54
|
'component-container pb-2 mt-0 lg:mt-4 ios-wrapper',
|
|
60
55
|
{ 'w-full': landscapeIos }
|
|
61
|
-
]"
|
|
62
|
-
/>
|
|
56
|
+
]" />
|
|
63
57
|
</transition>
|
|
64
58
|
</router-view>
|
|
65
59
|
</div>
|
|
@@ -94,10 +88,27 @@ function isIpadOS() {
|
|
|
94
88
|
return navigator.maxTouchPoints && navigator.maxTouchPoints > 2 && /MacIntel/.test(navigator.platform);
|
|
95
89
|
}
|
|
96
90
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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);
|
|
101
112
|
|
|
102
113
|
if (!window.location.href.includes(":5173")) ui.startSpinner("Loading...");
|
|
103
114
|
|
|
@@ -109,7 +120,7 @@ onMounted(() => {
|
|
|
109
120
|
setTimeout(handleNotch, 50);
|
|
110
121
|
setTimeout(handleNotch, 150);
|
|
111
122
|
setTimeout(handleNotch, 300);
|
|
112
|
-
|
|
123
|
+
|
|
113
124
|
// Set up a recurring check for the first few seconds
|
|
114
125
|
let attempts = 0;
|
|
115
126
|
const recurringCheck = setInterval(() => {
|
|
@@ -149,64 +160,30 @@ document.addEventListener("keydown", function (event) {
|
|
|
149
160
|
}
|
|
150
161
|
});
|
|
151
162
|
// Nuclear iOS keyboard prevention - lock everything down
|
|
152
|
-
let isIOSDevice =
|
|
163
|
+
let isIOSDevice =
|
|
164
|
+
/iPad|iPhone|iPod/.test(navigator.platform) ||
|
|
165
|
+
(navigator.maxTouchPoints && navigator.maxTouchPoints > 2 && /MacIntel/.test(navigator.platform));
|
|
153
166
|
|
|
154
167
|
if (isIOSDevice) {
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const lockViewport = () => {
|
|
160
|
-
document.documentElement.style.setProperty('height', `${originalViewportHeight}px`, 'important');
|
|
161
|
-
document.documentElement.style.setProperty('width', '100vw', 'important');
|
|
162
|
-
document.documentElement.style.setProperty('position', 'fixed', 'important');
|
|
163
|
-
document.documentElement.style.setProperty('top', `${-originalScrollY}px`, 'important');
|
|
164
|
-
document.documentElement.style.setProperty('left', '0px', 'important');
|
|
165
|
-
document.documentElement.style.setProperty('overflow', 'hidden', 'important');
|
|
166
|
-
|
|
167
|
-
document.body.style.setProperty('height', `${originalViewportHeight}px`, 'important');
|
|
168
|
-
document.body.style.setProperty('width', '100vw', 'important');
|
|
169
|
-
document.body.style.setProperty('position', 'fixed', 'important');
|
|
170
|
-
document.body.style.setProperty('top', '0px', 'important');
|
|
171
|
-
document.body.style.setProperty('left', '0px', 'important');
|
|
172
|
-
document.body.style.setProperty('overflow', 'hidden', 'important');
|
|
173
|
-
|
|
174
|
-
const app = document.getElementById('app');
|
|
175
|
-
if (app) {
|
|
176
|
-
app.style.setProperty('height', `${originalViewportHeight}px`, 'important');
|
|
177
|
-
app.style.setProperty('width', '100vw', 'important');
|
|
178
|
-
app.style.setProperty('overflow', 'hidden', 'important');
|
|
168
|
+
const handleViewportResize = () => {
|
|
169
|
+
if (isIpadOS()) {
|
|
170
|
+
// For iPad, allow natural viewport behavior
|
|
171
|
+
return;
|
|
179
172
|
}
|
|
173
|
+
|
|
174
|
+
// For iPhone, maintain viewport stability
|
|
175
|
+
const vh = window.innerHeight * 0.01;
|
|
176
|
+
document.documentElement.style.setProperty("--vh", `${vh}px`);
|
|
180
177
|
};
|
|
181
|
-
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
//
|
|
186
|
-
window.addEventListener(
|
|
187
|
-
e.preventDefault();
|
|
188
|
-
e.stopPropagation();
|
|
189
|
-
lockViewport();
|
|
190
|
-
return false;
|
|
191
|
-
}, { passive: false, capture: true });
|
|
192
|
-
|
|
178
|
+
|
|
179
|
+
// Initial setup
|
|
180
|
+
handleViewportResize();
|
|
181
|
+
|
|
182
|
+
// Listen for viewport changes
|
|
183
|
+
window.addEventListener("resize", handleViewportResize);
|
|
193
184
|
if (window.visualViewport) {
|
|
194
|
-
window.visualViewport.addEventListener(
|
|
195
|
-
e.preventDefault();
|
|
196
|
-
e.stopPropagation();
|
|
197
|
-
lockViewport();
|
|
198
|
-
return false;
|
|
199
|
-
}, { passive: false });
|
|
185
|
+
window.visualViewport.addEventListener("resize", handleViewportResize);
|
|
200
186
|
}
|
|
201
|
-
|
|
202
|
-
// Override window.innerHeight
|
|
203
|
-
Object.defineProperty(window, 'innerHeight', {
|
|
204
|
-
get: () => originalViewportHeight,
|
|
205
|
-
configurable: false
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
// Continuous enforcement
|
|
209
|
-
setInterval(lockViewport, 200);
|
|
210
187
|
}
|
|
211
188
|
|
|
212
189
|
// Precise mouse wheel control - only allow scrolling within specific scrollable elements
|
|
@@ -214,22 +191,22 @@ window.addEventListener(
|
|
|
214
191
|
"mousewheel",
|
|
215
192
|
function (event) {
|
|
216
193
|
// Check if we're on a scrollable textarea
|
|
217
|
-
const isScrollableTextarea =
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
194
|
+
const isScrollableTextarea =
|
|
195
|
+
event.target.tagName === "TEXTAREA" &&
|
|
196
|
+
(event.target.classList.contains("code-editor") || event.target.classList.contains("proxy-editor"));
|
|
197
|
+
|
|
221
198
|
// Check if we're in a table but only allow scrolling on actual scrollable content
|
|
222
|
-
const isInTable = event.target.closest(
|
|
223
|
-
const isScrollableTableContent =
|
|
224
|
-
|
|
225
|
-
event.target.closest(
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
+
|
|
229
206
|
// Allow scrolling in navbar and modals
|
|
230
|
-
const isInNavbar = event.target.closest(
|
|
207
|
+
const isInNavbar = event.target.closest(".navbar") || event.target.closest(".mobile-menu");
|
|
231
208
|
const isInModal = event.target.closest('[role="dialog"]');
|
|
232
|
-
|
|
209
|
+
|
|
233
210
|
// Only allow these specific cases
|
|
234
211
|
if (isScrollableTextarea || isScrollableTableContent || isInNavbar || isInModal) {
|
|
235
212
|
// Stop propagation to prevent page scroll
|
|
@@ -238,7 +215,7 @@ window.addEventListener(
|
|
|
238
215
|
}
|
|
239
216
|
return;
|
|
240
217
|
}
|
|
241
|
-
|
|
218
|
+
|
|
242
219
|
event.preventDefault();
|
|
243
220
|
},
|
|
244
221
|
{ passive: false }
|
|
@@ -248,27 +225,27 @@ window.addEventListener(
|
|
|
248
225
|
"DOMMouseScroll",
|
|
249
226
|
function (event) {
|
|
250
227
|
// Use same logic as mousewheel
|
|
251
|
-
const isScrollableTextarea =
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const isInTable = event.target.closest(
|
|
256
|
-
const isScrollableTableContent =
|
|
257
|
-
|
|
258
|
-
event.target.closest(
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const isInNavbar = event.target.closest(
|
|
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");
|
|
263
240
|
const isInModal = event.target.closest('[role="dialog"]');
|
|
264
|
-
|
|
241
|
+
|
|
265
242
|
if (isScrollableTextarea || isScrollableTableContent || isInNavbar || isInModal) {
|
|
266
243
|
if (isScrollableTableContent || isScrollableTextarea) {
|
|
267
244
|
event.stopPropagation();
|
|
268
245
|
}
|
|
269
246
|
return;
|
|
270
247
|
}
|
|
271
|
-
|
|
248
|
+
|
|
272
249
|
event.preventDefault();
|
|
273
250
|
},
|
|
274
251
|
{ passive: false }
|
|
@@ -278,27 +255,27 @@ window.addEventListener(
|
|
|
278
255
|
"wheel",
|
|
279
256
|
function (event) {
|
|
280
257
|
// Use same logic as mousewheel
|
|
281
|
-
const isScrollableTextarea =
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
const isInTable = event.target.closest(
|
|
286
|
-
const isScrollableTableContent =
|
|
287
|
-
|
|
288
|
-
event.target.closest(
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const isInNavbar = event.target.closest(
|
|
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");
|
|
293
270
|
const isInModal = event.target.closest('[role="dialog"]');
|
|
294
|
-
|
|
271
|
+
|
|
295
272
|
if (isScrollableTextarea || isScrollableTableContent || isInNavbar || isInModal) {
|
|
296
273
|
if (isScrollableTableContent || isScrollableTextarea) {
|
|
297
274
|
event.stopPropagation();
|
|
298
275
|
}
|
|
299
276
|
return;
|
|
300
277
|
}
|
|
301
|
-
|
|
278
|
+
|
|
302
279
|
event.preventDefault();
|
|
303
280
|
},
|
|
304
281
|
{ passive: false }
|
|
@@ -317,22 +294,22 @@ window.addEventListener(
|
|
|
317
294
|
"touchmove",
|
|
318
295
|
function (event) {
|
|
319
296
|
// Check if we're touching a scrollable element directly
|
|
320
|
-
const isScrollableTextarea =
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
297
|
+
const isScrollableTextarea =
|
|
298
|
+
event.target.tagName === "TEXTAREA" &&
|
|
299
|
+
(event.target.classList.contains("code-editor") || event.target.classList.contains("proxy-editor"));
|
|
300
|
+
|
|
324
301
|
// Check if we're in a table but only allow scrolling on actual scrollable content
|
|
325
|
-
const isInTable = event.target.closest(
|
|
326
|
-
const isScrollableTableContent =
|
|
327
|
-
|
|
328
|
-
event.target.closest(
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
+
|
|
332
309
|
// Allow scrolling in navbar and modals (they handle their own boundaries)
|
|
333
|
-
const isInNavbar = event.target.closest(
|
|
310
|
+
const isInNavbar = event.target.closest(".navbar") || event.target.closest(".mobile-menu");
|
|
334
311
|
const isInModal = event.target.closest('[role="dialog"]');
|
|
335
|
-
|
|
312
|
+
|
|
336
313
|
// Only allow these specific cases
|
|
337
314
|
if (isScrollableTextarea || isScrollableTableContent || isInNavbar || isInModal) {
|
|
338
315
|
// For table content, ensure we stop propagation to prevent page scroll
|
|
@@ -341,7 +318,7 @@ window.addEventListener(
|
|
|
341
318
|
}
|
|
342
319
|
return;
|
|
343
320
|
}
|
|
344
|
-
|
|
321
|
+
|
|
345
322
|
// Prevent everything else
|
|
346
323
|
event.preventDefault();
|
|
347
324
|
},
|
|
@@ -358,22 +335,22 @@ let pendingUpdate = false;
|
|
|
358
335
|
function isLandscapeMode() {
|
|
359
336
|
// Check orientation first
|
|
360
337
|
let orientationLandscape = false;
|
|
361
|
-
|
|
362
|
-
if (typeof window.orientation !==
|
|
338
|
+
|
|
339
|
+
if (typeof window.orientation !== "undefined") {
|
|
363
340
|
orientationLandscape = Math.abs(window.orientation) === 90;
|
|
364
|
-
} else if (screen.orientation && typeof screen.orientation.angle !==
|
|
341
|
+
} else if (screen.orientation && typeof screen.orientation.angle !== "undefined") {
|
|
365
342
|
orientationLandscape = Math.abs(screen.orientation.angle) === 90;
|
|
366
343
|
}
|
|
367
|
-
|
|
344
|
+
|
|
368
345
|
// Always also check dimensions as backup
|
|
369
346
|
const dimensionLandscape = window.innerWidth > window.innerHeight;
|
|
370
|
-
const hasStrongLandscapeRatio =
|
|
371
|
-
|
|
347
|
+
const hasStrongLandscapeRatio = window.innerWidth / window.innerHeight > 1.5;
|
|
348
|
+
|
|
372
349
|
// For iPhones, if dimensions clearly indicate landscape, trust that
|
|
373
350
|
if (isIOS() && !isIpadOS() && dimensionLandscape && hasStrongLandscapeRatio) {
|
|
374
351
|
return true;
|
|
375
352
|
}
|
|
376
|
-
|
|
353
|
+
|
|
377
354
|
// Otherwise use orientation if available, fallback to dimensions
|
|
378
355
|
return orientationLandscape || (dimensionLandscape && hasStrongLandscapeRatio);
|
|
379
356
|
}
|
|
@@ -381,46 +358,48 @@ function isLandscapeMode() {
|
|
|
381
358
|
function hasDeviceNotch() {
|
|
382
359
|
// Only check for notch on actual devices that might have one
|
|
383
360
|
if (!isIOS() || isIpadOS()) return false;
|
|
384
|
-
|
|
385
|
-
return
|
|
386
|
-
|
|
361
|
+
|
|
362
|
+
return (
|
|
363
|
+
CSS.supports("padding-left: env(safe-area-inset-left)") &&
|
|
364
|
+
CSS.supports("padding-right: env(safe-area-inset-right)")
|
|
365
|
+
);
|
|
387
366
|
}
|
|
388
367
|
|
|
389
368
|
function handleNotch(force = false) {
|
|
390
369
|
// Simple debouncing
|
|
391
370
|
if (isNotchBusy && !force) return;
|
|
392
|
-
|
|
371
|
+
|
|
393
372
|
try {
|
|
394
373
|
// Only for iPhone (not iPad)
|
|
395
374
|
if (!isIOS() || isIpadOS()) return;
|
|
396
|
-
|
|
375
|
+
|
|
397
376
|
const wrappers = document.querySelectorAll(".ios-wrapper");
|
|
398
377
|
if (wrappers.length === 0) {
|
|
399
378
|
setTimeout(() => handleNotch(force), 100);
|
|
400
379
|
return;
|
|
401
380
|
}
|
|
402
|
-
|
|
381
|
+
|
|
403
382
|
const isLandscape = isLandscapeMode();
|
|
404
383
|
const hasNotch = hasDeviceNotch();
|
|
405
|
-
|
|
384
|
+
|
|
406
385
|
// Get orientation
|
|
407
386
|
let orientation = window.orientation;
|
|
408
|
-
if (typeof orientation ===
|
|
387
|
+
if (typeof orientation === "undefined" && screen.orientation) {
|
|
409
388
|
orientation = screen.orientation.angle;
|
|
410
389
|
}
|
|
411
|
-
|
|
390
|
+
|
|
412
391
|
// Create state signature to prevent redundant updates
|
|
413
392
|
const currentState = `${isLandscape}-${hasNotch}-${orientation}-${window.innerWidth}x${window.innerHeight}`;
|
|
414
393
|
if (lastNotchState === currentState && !force) {
|
|
415
394
|
return;
|
|
416
395
|
}
|
|
417
396
|
lastNotchState = currentState;
|
|
418
|
-
|
|
397
|
+
|
|
419
398
|
isNotchBusy = true;
|
|
420
|
-
|
|
399
|
+
|
|
421
400
|
// Debug info
|
|
422
|
-
if (window.location.href.includes(
|
|
423
|
-
console.log(
|
|
401
|
+
if (window.location.href.includes("localhost") || window.location.href.includes("5173")) {
|
|
402
|
+
console.log("🔥 Notch Debug:", {
|
|
424
403
|
isLandscape,
|
|
425
404
|
hasNotch,
|
|
426
405
|
orientation,
|
|
@@ -428,34 +407,34 @@ function handleNotch(force = false) {
|
|
|
428
407
|
state: currentState
|
|
429
408
|
});
|
|
430
409
|
}
|
|
431
|
-
|
|
410
|
+
|
|
432
411
|
if (hasNotch && isLandscape) {
|
|
433
412
|
// Test actual safe area values to determine notch side
|
|
434
|
-
const testDiv = document.createElement(
|
|
435
|
-
testDiv.style.position =
|
|
436
|
-
testDiv.style.top =
|
|
437
|
-
testDiv.style.left =
|
|
438
|
-
testDiv.style.visibility =
|
|
439
|
-
testDiv.style.paddingLeft =
|
|
440
|
-
testDiv.style.paddingRight =
|
|
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)";
|
|
441
420
|
document.body.appendChild(testDiv);
|
|
442
|
-
|
|
421
|
+
|
|
443
422
|
const computedStyle = getComputedStyle(testDiv);
|
|
444
423
|
const leftInset = parseFloat(computedStyle.paddingLeft) || 0;
|
|
445
424
|
const rightInset = parseFloat(computedStyle.paddingRight) || 0;
|
|
446
|
-
|
|
425
|
+
|
|
447
426
|
document.body.removeChild(testDiv);
|
|
448
|
-
|
|
449
|
-
console.log(
|
|
450
|
-
|
|
427
|
+
|
|
428
|
+
console.log("🔍 Safe area insets:", { leftInset, rightInset });
|
|
429
|
+
|
|
451
430
|
// Apply styles instantly - NO ANIMATION
|
|
452
|
-
wrappers.forEach(wrapper => {
|
|
431
|
+
wrappers.forEach((wrapper) => {
|
|
453
432
|
if (leftInset > 0) {
|
|
454
433
|
// Notch on left
|
|
455
434
|
wrapper.style.paddingLeft = "env(safe-area-inset-left)";
|
|
456
435
|
wrapper.style.paddingRight = "0.5rem";
|
|
457
436
|
} else if (rightInset > 0) {
|
|
458
|
-
// Notch on right
|
|
437
|
+
// Notch on right
|
|
459
438
|
wrapper.style.paddingLeft = "0.5rem";
|
|
460
439
|
wrapper.style.paddingRight = "env(safe-area-inset-right)";
|
|
461
440
|
} else {
|
|
@@ -466,21 +445,25 @@ function handleNotch(force = false) {
|
|
|
466
445
|
});
|
|
467
446
|
} else {
|
|
468
447
|
// Portrait or no notch
|
|
469
|
-
const padding =
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
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
|
+
|
|
473
457
|
// Apply styles instantly - NO ANIMATION
|
|
474
|
-
wrappers.forEach(wrapper => {
|
|
458
|
+
wrappers.forEach((wrapper) => {
|
|
475
459
|
wrapper.style.paddingLeft = padding;
|
|
476
460
|
wrapper.style.paddingRight = padding;
|
|
477
461
|
});
|
|
478
462
|
}
|
|
479
|
-
|
|
463
|
+
|
|
480
464
|
isNotchBusy = false;
|
|
481
|
-
|
|
482
465
|
} catch (error) {
|
|
483
|
-
console.warn(
|
|
466
|
+
console.warn("Notch error:", error);
|
|
484
467
|
isNotchBusy = false;
|
|
485
468
|
}
|
|
486
469
|
}
|
|
@@ -491,10 +474,10 @@ function triggerNotch() {
|
|
|
491
474
|
}
|
|
492
475
|
|
|
493
476
|
// Event listeners
|
|
494
|
-
window.addEventListener(
|
|
495
|
-
window.addEventListener(
|
|
477
|
+
window.addEventListener("orientationchange", triggerNotch);
|
|
478
|
+
window.addEventListener("resize", triggerNotch);
|
|
496
479
|
if (screen.orientation) {
|
|
497
|
-
screen.orientation.addEventListener(
|
|
480
|
+
screen.orientation.addEventListener("change", triggerNotch);
|
|
498
481
|
}
|
|
499
482
|
|
|
500
483
|
// Aggressive initial setup to handle all scenarios
|
|
@@ -509,43 +492,42 @@ function initNotchHandling() {
|
|
|
509
492
|
}
|
|
510
493
|
|
|
511
494
|
// Multiple initialization points
|
|
512
|
-
if (document.readyState ===
|
|
513
|
-
document.addEventListener(
|
|
495
|
+
if (document.readyState === "loading") {
|
|
496
|
+
document.addEventListener("DOMContentLoaded", initNotchHandling);
|
|
514
497
|
} else {
|
|
515
498
|
initNotchHandling();
|
|
516
499
|
}
|
|
517
500
|
|
|
518
|
-
window.addEventListener(
|
|
501
|
+
window.addEventListener("load", initNotchHandling);
|
|
519
502
|
|
|
520
503
|
// Also run when page becomes visible (handles app switching)
|
|
521
|
-
document.addEventListener(
|
|
504
|
+
document.addEventListener("visibilitychange", () => {
|
|
522
505
|
if (!document.hidden) {
|
|
523
506
|
setTimeout(handleNotch, 100);
|
|
524
507
|
}
|
|
525
508
|
});
|
|
526
509
|
|
|
527
510
|
// Run on focus (when returning to app)
|
|
528
|
-
window.addEventListener(
|
|
511
|
+
window.addEventListener("focus", () => {
|
|
529
512
|
setTimeout(handleNotch, 100);
|
|
530
513
|
});
|
|
531
514
|
|
|
532
515
|
// Watch for ios-wrapper elements appearing in DOM
|
|
533
516
|
const observer = new MutationObserver((mutations) => {
|
|
534
517
|
let shouldTrigger = false;
|
|
535
|
-
|
|
518
|
+
|
|
536
519
|
mutations.forEach((mutation) => {
|
|
537
|
-
if (mutation.type ===
|
|
520
|
+
if (mutation.type === "childList") {
|
|
538
521
|
mutation.addedNodes.forEach((node) => {
|
|
539
522
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
540
|
-
if (node.classList?.contains(
|
|
541
|
-
node.querySelector?.('.ios-wrapper')) {
|
|
523
|
+
if (node.classList?.contains("ios-wrapper") || node.querySelector?.(".ios-wrapper")) {
|
|
542
524
|
shouldTrigger = true;
|
|
543
525
|
}
|
|
544
526
|
}
|
|
545
527
|
});
|
|
546
528
|
}
|
|
547
529
|
});
|
|
548
|
-
|
|
530
|
+
|
|
549
531
|
if (shouldTrigger) {
|
|
550
532
|
setTimeout(() => handleNotch(true), 10);
|
|
551
533
|
setTimeout(() => handleNotch(true), 100);
|
|
@@ -554,15 +536,15 @@ const observer = new MutationObserver((mutations) => {
|
|
|
554
536
|
|
|
555
537
|
// Start observing
|
|
556
538
|
if (document.body) {
|
|
557
|
-
observer.observe(document.body, {
|
|
558
|
-
childList: true,
|
|
559
|
-
subtree: true
|
|
539
|
+
observer.observe(document.body, {
|
|
540
|
+
childList: true,
|
|
541
|
+
subtree: true
|
|
560
542
|
});
|
|
561
543
|
} else {
|
|
562
|
-
document.addEventListener(
|
|
563
|
-
observer.observe(document.body, {
|
|
564
|
-
childList: true,
|
|
565
|
-
subtree: true
|
|
544
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
545
|
+
observer.observe(document.body, {
|
|
546
|
+
childList: true,
|
|
547
|
+
subtree: true
|
|
566
548
|
});
|
|
567
549
|
});
|
|
568
550
|
}
|
|
@@ -571,17 +553,17 @@ if (document.body) {
|
|
|
571
553
|
const originalPushState = history.pushState;
|
|
572
554
|
const originalReplaceState = history.replaceState;
|
|
573
555
|
|
|
574
|
-
history.pushState = function(...args) {
|
|
556
|
+
history.pushState = function (...args) {
|
|
575
557
|
originalPushState.apply(this, args);
|
|
576
558
|
triggerNotch();
|
|
577
559
|
};
|
|
578
560
|
|
|
579
|
-
history.replaceState = function(...args) {
|
|
561
|
+
history.replaceState = function (...args) {
|
|
580
562
|
originalReplaceState.apply(this, args);
|
|
581
563
|
triggerNotch();
|
|
582
564
|
};
|
|
583
565
|
|
|
584
|
-
window.addEventListener(
|
|
566
|
+
window.addEventListener("popstate", triggerNotch);
|
|
585
567
|
|
|
586
568
|
// Expose for manual triggering
|
|
587
569
|
window.simulateRotate = handleNotch;
|
|
@@ -645,7 +627,7 @@ watch(
|
|
|
645
627
|
// Immediate triggers
|
|
646
628
|
handleNotch();
|
|
647
629
|
triggerNotch();
|
|
648
|
-
|
|
630
|
+
|
|
649
631
|
// Staggered attempts
|
|
650
632
|
setTimeout(handleNotch, 10);
|
|
651
633
|
setTimeout(handleNotch, 50);
|
|
@@ -658,10 +640,10 @@ watch(
|
|
|
658
640
|
watch(
|
|
659
641
|
() => router.currentRoute.value.path,
|
|
660
642
|
() => {
|
|
661
|
-
// Immediate triggers
|
|
643
|
+
// Immediate triggers
|
|
662
644
|
handleNotch();
|
|
663
645
|
triggerNotch();
|
|
664
|
-
|
|
646
|
+
|
|
665
647
|
// Staggered attempts
|
|
666
648
|
setTimeout(handleNotch, 25);
|
|
667
649
|
setTimeout(handleNotch, 100);
|
|
@@ -672,7 +654,8 @@ const layout = computed(() => router.currentRoute.value.meta.layout);
|
|
|
672
654
|
</script>
|
|
673
655
|
<style lang="scss">
|
|
674
656
|
// Ultra bulletproof scroll prevention
|
|
675
|
-
html,
|
|
657
|
+
html,
|
|
658
|
+
body {
|
|
676
659
|
overflow: hidden !important;
|
|
677
660
|
overscroll-behavior: none !important;
|
|
678
661
|
width: 100% !important;
|