4sp-dv 1.0.25 → 1.0.27
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/4simpleproblems_v5.html +2 -2
- package/logged-in/weather.html +174 -364
- package/package.json +1 -1
package/4simpleproblems_v5.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>4SP - VERSION 5 CLIENT</title>
|
|
7
7
|
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/npm/4sp-dv@latest/images/logo.png">
|
|
8
8
|
|
|
9
|
-
<base href="https://cdn.jsdelivr.net/npm/4sp-dv@1.0.
|
|
9
|
+
<base href="https://cdn.jsdelivr.net/npm/4sp-dv@1.0.27/logged-in/">
|
|
10
10
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
11
11
|
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@100..900&display=swap" rel="stylesheet">
|
|
12
12
|
|
|
@@ -774,7 +774,7 @@
|
|
|
774
774
|
const displayUsername = document.getElementById('display-username');
|
|
775
775
|
const displayEmail = document.getElementById('display-email');
|
|
776
776
|
|
|
777
|
-
const BASE_URL = 'https://cdn.jsdelivr.net/npm/4sp-dv@1.0.
|
|
777
|
+
const BASE_URL = 'https://cdn.jsdelivr.net/npm/4sp-dv@1.0.27/logged-in/';
|
|
778
778
|
const STORAGE_KEY = 'local_access_code';
|
|
779
779
|
const USER_DATA_KEY = 'local_user_data';
|
|
780
780
|
const LAST_PAGE_KEY = 'local_last_page';
|
package/logged-in/weather.html
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@100..900&display=swap" rel="stylesheet">
|
|
12
12
|
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
|
13
13
|
|
|
14
|
-
<!-- FontAwesome 6.5.2 (Free CDN) -->
|
|
15
14
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">
|
|
15
|
+
|
|
16
16
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
17
17
|
<script src="../navigation.js"></script>
|
|
18
18
|
<script src="../injector.js"></script>
|
|
@@ -20,125 +20,126 @@
|
|
|
20
20
|
<style>
|
|
21
21
|
/* --- 4SP BASE STYLING --- */
|
|
22
22
|
:root {
|
|
23
|
+
--bg-page: #040404;
|
|
24
|
+
--bg-container: #000000;
|
|
25
|
+
--border-color: #333;
|
|
26
|
+
--text-primary: #c0c0c0;
|
|
27
|
+
--color-indigo: #4f46e5;
|
|
23
28
|
--menu-bg: #000000;
|
|
24
29
|
--menu-border: #333;
|
|
25
|
-
--menu-text: #d1d5db;
|
|
26
|
-
--menu-item-hover-bg: #2a2a2a;
|
|
27
|
-
--accent-bg: rgba(79, 70, 229, 0.1);
|
|
28
|
-
--accent-border: #4f46e5;
|
|
29
|
-
--accent-text: #6366f1;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
body {
|
|
33
33
|
font-family: 'Geist', sans-serif;
|
|
34
|
-
background-color:
|
|
35
|
-
color:
|
|
34
|
+
background-color: var(--bg-page);
|
|
35
|
+
color: var(--text-primary);
|
|
36
36
|
transition: all 0.3s ease;
|
|
37
37
|
font-size: 16px;
|
|
38
38
|
overflow-x: hidden;
|
|
39
39
|
font-weight: 300;
|
|
40
|
+
min-height: 100vh;
|
|
41
|
+
display: flex;
|
|
42
|
+
flex-direction: column;
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
h1, h2, h3, .font-bold, .font-semibold, strong {
|
|
43
|
-
font-weight: 400 !important;
|
|
46
|
+
font-weight: 400 !important;
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
/* ---
|
|
47
|
-
.btn-toolbar {
|
|
48
|
-
background: var(--menu-bg);
|
|
49
|
-
border: 1px solid var(--menu-border);
|
|
49
|
+
/* --- UI COMPONENTS --- */
|
|
50
|
+
.btn-toolbar-style {
|
|
51
|
+
background: var(--menu-bg, #000000);
|
|
52
|
+
border: 1px solid var(--menu-border, #333);
|
|
50
53
|
border-radius: 0.75rem;
|
|
51
|
-
color:
|
|
52
|
-
padding: 0.
|
|
54
|
+
color: #d1d5db;
|
|
55
|
+
padding: 0.6rem 1.2rem;
|
|
53
56
|
font-weight: 500;
|
|
54
57
|
cursor: pointer;
|
|
55
58
|
transition: all 0.2s;
|
|
56
59
|
display: inline-flex;
|
|
57
60
|
align-items: center;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
background-color: var(--menu-item-hover-bg);
|
|
63
|
-
border-color: #555;
|
|
64
|
-
color: #fff;
|
|
65
|
-
}
|
|
66
|
-
.btn-toolbar.active {
|
|
67
|
-
background-color: var(--accent-bg);
|
|
68
|
-
border-color: var(--accent-border);
|
|
69
|
-
color: var(--accent-text);
|
|
61
|
+
justify-content: center;
|
|
62
|
+
gap: 0.75rem;
|
|
63
|
+
font-size: 0.95rem;
|
|
64
|
+
white-space: nowrap;
|
|
70
65
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
color: #fff;
|
|
78
|
-
padding: 0.5rem 1rem;
|
|
79
|
-
padding-right: 2.5rem; /* Space for icon */
|
|
80
|
-
font-weight: 400;
|
|
81
|
-
width: 260px;
|
|
82
|
-
transition: all 0.2s;
|
|
83
|
-
outline: none;
|
|
66
|
+
|
|
67
|
+
.btn-toolbar-style:hover {
|
|
68
|
+
background-color: #000000;
|
|
69
|
+
border-color: #fff;
|
|
70
|
+
color: #ffffff;
|
|
71
|
+
transform: translateY(-1px);
|
|
84
72
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
73
|
+
|
|
74
|
+
.provider-select-wrapper {
|
|
75
|
+
position: relative;
|
|
76
|
+
display: inline-block;
|
|
77
|
+
min-width: 180px; /* Minimum width */
|
|
88
78
|
}
|
|
89
79
|
|
|
90
|
-
.
|
|
91
|
-
|
|
92
|
-
top: 100%;
|
|
93
|
-
left: 0;
|
|
94
|
-
right: 0;
|
|
95
|
-
margin-top: 0.5rem;
|
|
80
|
+
.provider-select {
|
|
81
|
+
appearance: none;
|
|
96
82
|
background: #0d0d0d;
|
|
97
83
|
border: 1px solid #333;
|
|
84
|
+
color: #fff;
|
|
85
|
+
padding: 0.6rem 2.5rem 0.6rem 1rem;
|
|
98
86
|
border-radius: 0.75rem;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
.search-dropdown.active {
|
|
106
|
-
display: block;
|
|
87
|
+
font-family: 'Geist', sans-serif;
|
|
88
|
+
font-size: 0.95rem;
|
|
89
|
+
cursor: pointer;
|
|
90
|
+
width: auto; /* Allow width to change based on content */
|
|
91
|
+
min-width: 100%;
|
|
92
|
+
transition: border-color 0.2s;
|
|
107
93
|
}
|
|
108
94
|
|
|
109
|
-
.
|
|
110
|
-
|
|
111
|
-
cursor: pointer;
|
|
112
|
-
border-bottom: 1px solid #1a1a1a;
|
|
113
|
-
display: flex;
|
|
114
|
-
flex-direction: column;
|
|
115
|
-
gap: 2px;
|
|
116
|
-
transition: background 0.2s;
|
|
95
|
+
.provider-select:hover {
|
|
96
|
+
border-color: #6366f1;
|
|
117
97
|
}
|
|
118
|
-
.search-item:last-child { border-bottom: none; }
|
|
119
|
-
.search-item:hover { background: #1a1a1a; }
|
|
120
|
-
.search-item-main { color: #fff; font-size: 0.95rem; }
|
|
121
|
-
.search-item-sub { color: #707070; font-size: 0.8rem; }
|
|
122
98
|
|
|
123
|
-
.
|
|
99
|
+
.provider-select:focus {
|
|
100
|
+
outline: none;
|
|
101
|
+
border-color: #6366f1;
|
|
102
|
+
box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.select-icon {
|
|
124
106
|
position: absolute;
|
|
125
|
-
right:
|
|
107
|
+
right: 1rem;
|
|
126
108
|
top: 50%;
|
|
127
109
|
transform: translateY(-50%);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
110
|
+
pointer-events: none;
|
|
111
|
+
color: #6b7280;
|
|
112
|
+
font-size: 0.75rem;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* --- LAYOUT UTILS --- */
|
|
116
|
+
#page-content {
|
|
117
|
+
flex-grow: 1;
|
|
118
|
+
display: flex;
|
|
119
|
+
flex-direction: column;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
header {
|
|
123
|
+
display: flex;
|
|
124
|
+
flex-direction: column;
|
|
125
|
+
gap: 1.5rem;
|
|
126
|
+
padding: 1.5rem;
|
|
127
|
+
border-bottom: 1px solid #1a1a1a;
|
|
128
|
+
background-color: var(--bg-page);
|
|
129
|
+
}
|
|
130
|
+
@media(min-width: 768px) {
|
|
131
|
+
header {
|
|
132
|
+
flex-direction: row;
|
|
133
|
+
justify-content: space-between;
|
|
134
|
+
align-items: center;
|
|
135
|
+
}
|
|
133
136
|
}
|
|
134
|
-
.geo-btn:hover { color: var(--accent-text); }
|
|
135
|
-
.geo-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
136
137
|
|
|
137
138
|
/* --- WEATHER CARDS --- */
|
|
138
139
|
.weather-card {
|
|
139
140
|
background-color: #0d0d0d;
|
|
140
141
|
border: 1px solid #1a1a1a;
|
|
141
|
-
border-radius:
|
|
142
|
+
border-radius: 1.5rem;
|
|
142
143
|
padding: 1.5rem;
|
|
143
144
|
transition: border-color 0.2s;
|
|
144
145
|
}
|
|
@@ -149,42 +150,52 @@
|
|
|
149
150
|
/* Hero Section */
|
|
150
151
|
.current-temp {
|
|
151
152
|
font-family: 'Roboto', sans-serif;
|
|
152
|
-
font-size:
|
|
153
|
+
font-size: 4rem;
|
|
153
154
|
font-weight: 300;
|
|
154
155
|
color: #fff;
|
|
155
156
|
line-height: 1;
|
|
156
157
|
}
|
|
157
158
|
.current-icon {
|
|
158
|
-
font-size:
|
|
159
|
-
color: var(--
|
|
159
|
+
font-size: 3rem;
|
|
160
|
+
color: var(--color-indigo);
|
|
160
161
|
}
|
|
161
162
|
|
|
162
163
|
/* Hourly Scroll */
|
|
164
|
+
.scroll-mask-container {
|
|
165
|
+
position: relative;
|
|
166
|
+
width: 100%;
|
|
167
|
+
-webkit-mask-image: linear-gradient(to right, transparent 0%, black 5%, black 95%, transparent 100%);
|
|
168
|
+
mask-image: linear-gradient(to right, transparent 0%, black 5%, black 95%, transparent 100%);
|
|
169
|
+
}
|
|
170
|
+
|
|
163
171
|
.hourly-container {
|
|
164
172
|
display: flex;
|
|
165
173
|
overflow-x: auto;
|
|
166
|
-
gap:
|
|
167
|
-
padding
|
|
168
|
-
margin-top:
|
|
169
|
-
scrollbar-width:
|
|
170
|
-
|
|
174
|
+
gap: 0.75rem;
|
|
175
|
+
padding: 1rem 1.5rem;
|
|
176
|
+
margin-top: 0.5rem;
|
|
177
|
+
scrollbar-width: none;
|
|
178
|
+
-ms-overflow-style: none;
|
|
171
179
|
}
|
|
180
|
+
.hourly-container::-webkit-scrollbar { display: none; }
|
|
181
|
+
|
|
172
182
|
.hourly-item {
|
|
173
183
|
min-width: 80px;
|
|
174
184
|
background: #111;
|
|
175
185
|
border: 1px solid #222;
|
|
176
|
-
border-radius:
|
|
186
|
+
border-radius: 1rem;
|
|
177
187
|
padding: 1rem 0.5rem;
|
|
178
188
|
display: flex;
|
|
179
189
|
flex-direction: column;
|
|
180
190
|
align-items: center;
|
|
181
191
|
gap: 0.5rem;
|
|
182
|
-
cursor: pointer;
|
|
183
|
-
transition:
|
|
192
|
+
cursor: pointer;
|
|
193
|
+
transition: all 0.2s;
|
|
184
194
|
}
|
|
185
195
|
.hourly-item:hover {
|
|
186
196
|
background: #1a1a1a;
|
|
187
197
|
border-color: #333;
|
|
198
|
+
transform: translateY(-2px);
|
|
188
199
|
}
|
|
189
200
|
.hourly-time { font-size: 0.8rem; color: #707070; }
|
|
190
201
|
.hourly-temp { font-weight: 500; color: #fff; }
|
|
@@ -194,9 +205,12 @@
|
|
|
194
205
|
display: flex;
|
|
195
206
|
align-items: center;
|
|
196
207
|
justify-content: space-between;
|
|
197
|
-
padding: 1rem 0;
|
|
208
|
+
padding: 1rem 0.5rem;
|
|
198
209
|
border-bottom: 1px solid #1a1a1a;
|
|
210
|
+
transition: background 0.2s;
|
|
211
|
+
border-radius: 1rem;
|
|
199
212
|
}
|
|
213
|
+
.daily-row:hover { background-color: #111; }
|
|
200
214
|
.daily-row:last-child { border-bottom: none; }
|
|
201
215
|
.daily-day { width: 100px; font-weight: 500; color: #fff; }
|
|
202
216
|
.daily-icon { width: 40px; text-align: center; color: #9ca3af; }
|
|
@@ -217,21 +231,12 @@
|
|
|
217
231
|
gap: 1rem;
|
|
218
232
|
transition: opacity 0.5s;
|
|
219
233
|
}
|
|
220
|
-
.spinner {
|
|
221
|
-
width: 40px;
|
|
222
|
-
height: 40px;
|
|
223
|
-
border: 3px solid #333;
|
|
224
|
-
border-top-color: var(--accent-border);
|
|
225
|
-
border-radius: 50%;
|
|
226
|
-
animation: spin 1s linear infinite;
|
|
227
|
-
}
|
|
228
|
-
@keyframes spin { to { transform: rotate(360deg); } }
|
|
229
234
|
|
|
230
235
|
/* --- MODAL (HOURLY) --- */
|
|
231
236
|
#hourly-modal-overlay {
|
|
232
237
|
position: fixed;
|
|
233
238
|
inset: 0;
|
|
234
|
-
background: rgba(0,0,0,0.
|
|
239
|
+
background: rgba(0,0,0,0.8);
|
|
235
240
|
backdrop-filter: blur(5px);
|
|
236
241
|
z-index: 60;
|
|
237
242
|
display: flex;
|
|
@@ -245,7 +250,7 @@
|
|
|
245
250
|
.modal-content {
|
|
246
251
|
background: #0d0d0d;
|
|
247
252
|
border: 1px solid #333;
|
|
248
|
-
border-radius:
|
|
253
|
+
border-radius: 2rem;
|
|
249
254
|
padding: 2rem;
|
|
250
255
|
width: 90%;
|
|
251
256
|
max-width: 400px;
|
|
@@ -253,82 +258,35 @@
|
|
|
253
258
|
display: flex;
|
|
254
259
|
flex-direction: column;
|
|
255
260
|
gap: 1.5rem;
|
|
261
|
+
transform: scale(0.95);
|
|
262
|
+
transition: transform 0.2s;
|
|
256
263
|
}
|
|
257
|
-
.modal-
|
|
264
|
+
#hourly-modal-overlay.active .modal-content { transform: scale(1); }
|
|
265
|
+
|
|
266
|
+
.modal-header-text { font-size: 1.25rem; color: #fff; font-weight: 400; }
|
|
258
267
|
.detail-row { display: flex; justify-content: space-between; border-bottom: 1px solid #1a1a1a; padding-bottom: 0.5rem; }
|
|
259
268
|
.detail-label { color: #707070; font-size: 0.9rem; }
|
|
260
269
|
.detail-value { color: #fff; font-weight: 500; }
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
/* Scrollbar */
|
|
274
|
-
::-webkit-scrollbar { width: 6px; height: 6px; }
|
|
275
|
-
::-webkit-scrollbar-track { background: #040404; }
|
|
276
|
-
::-webkit-scrollbar-thumb { background: #333; border-radius: 4px; }
|
|
277
|
-
::-webkit-scrollbar-thumb:hover { background: #555; }
|
|
278
|
-
|
|
279
|
-
/* INJECTED STYLES */
|
|
280
|
-
:root { --bg-page: #040404; --bg-container: #000000; --border-color: #333; --text-primary: #c0c0c0; --text-light-grey: #d1d5db; --accent-color: #6366f1; --navbar-bg: #000000; --navbar-border: #1f2937; --tab-text: #9ca3af; --tab-hover-text: #ffffff; --tab-active-text: #4f46e5; --tab-active-bg: rgba(79, 70, 229, 0.1); --tab-active-border: #4f46e5; }
|
|
281
|
-
@keyframes shake { 0% { transform: translateX(0); } 25% { transform: translateX(-5px) rotate(-5deg); } 50% { transform: translateX(5px) rotate(5deg); } 75% { transform: translateX(-5px) rotate(-5deg); } 100% { transform: translateX(0); } }
|
|
282
|
-
.shake-anim { animation: shake 0.4s cubic-bezier(.36,.07,.19,.97) both; }
|
|
283
|
-
body.no-animations * { transition: none !important; animation: none !important; transform: none !important; }
|
|
284
|
-
body:not(.no-animations) .btn-toolbar-style, body:not(.no-animations) .btn-lock-screen, body:not(.no-animations) .icon-btn, body:not(.no-animations) .nav-tab, body:not(.no-animations) .btn-card-action, body:not(.no-animations) .input-text-style, body:not(.no-animations) .input-lock-screen, body:not(.no-animations) .input-key-style { transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important; }
|
|
285
|
-
body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .btn-lock-screen:active, body:not(.no-animations) .icon-btn:active, body:not(.no-animations) .nav-tab:active, body:not(.no-animations) .btn-card-action:active { transform: scale(0.96); transition: transform 0.05s ease-out !important; }
|
|
286
|
-
.btn-toolbar-style { background: #0a0a0a; border: 1px solid #333; border-radius: 14px; color: #d1d5db; padding: 0.75rem 1.25rem; font-weight: 500; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.6); }
|
|
287
|
-
.btn-toolbar-style:hover { transform: scale(1.05) translateY(-2px); box-shadow: 0 6px 20px rgba(255, 255, 255, 0.1); border-color: #fff; color: #ffffff; }
|
|
288
|
-
.btn-toolbar-style.btn-primary-override { background-color: rgba(79, 70, 229, 0.1); border: 1px solid #4f46e5; color: #4f46e5; }
|
|
289
|
-
.btn-toolbar-style.btn-primary-override:hover { background-color: rgba(79, 70, 229, 0.2); border-color: #6366f1; color: #6366f1; box-shadow: 0 6px 20px rgba(79, 70, 229, 0.4); }
|
|
290
|
-
.btn-toolbar-style.btn-primary-override-danger { background-color: rgba(220, 38, 38, 0.1); border: 1px solid #dc2626; color: #dc2626; }
|
|
291
|
-
.btn-toolbar-style.btn-primary-override-danger:hover { background-color: rgba(220, 38, 38, 0.2); border-color: #ef4444; color: #ef4444; box-shadow: 0 6px 20px rgba(220, 38, 38, 0.4); }
|
|
292
|
-
.btn-lock-screen { padding: 0.75rem 1.25rem; font-size: 0.875rem; font-weight: 500; border-radius: 14px; color: rgb(99 102 241); background-color: rgba(99, 102, 241, 0.1); border: 1px solid rgb(99 102 241); box-shadow: 0 4px 15px rgba(0, 0, 0, 0.6); }
|
|
293
|
-
.btn-lock-screen:hover { transform: scale(1.05) translateY(-2px); box-shadow: 0 6px 20px rgba(79, 70, 229, 0.4); background-color: rgba(99, 102, 241, 0.2); }
|
|
294
|
-
.icon-btn { width: 40px; height: 40px; border-radius: 14px; border: 1px solid #4b5563; display: flex; align-items: center; justify-content: center; color: #d1d5db; cursor: pointer; background: transparent; }
|
|
295
|
-
.icon-btn:hover { background-color: #374151; color: white; transform: scale(1.1); }
|
|
296
|
-
.btn-card-action { display: inline-flex; align-items: center; justify-content: center; width: 2rem; height: 2rem; border-radius: 14px; background-color: transparent; border: 1px solid transparent; color: #9ca3af; cursor: pointer; }
|
|
297
|
-
.btn-card-action:hover { background-color: rgba(79, 70, 229, 0.1); color: #4f46e5; border-color: #4f46e5; transform: scale(1.15); }
|
|
298
|
-
.input-text-style, .input-select-style { width: 100%; padding: 0.75rem; border: 1px solid #252525; background-color: #111111; border-radius: 14px; color: #c0c0c0; }
|
|
299
|
-
.input-text-style:focus, .input-select-style:focus { border-color: #505050; outline: none; box-shadow: 0 0 0 2px rgba(80, 80, 80, 0.5); transform: scale(1.02); }
|
|
300
|
-
.input-lock-screen { background-color: #111; border: 1px solid #252525; color: white; border-radius: 14px; padding: 0.75rem; text-align: center; letter-spacing: 0.2em; outline: none; }
|
|
301
|
-
.input-lock-screen:focus { border-color: rgb(99 102 241); transform: scale(1.02); }
|
|
302
|
-
.input-key-style { width: 4rem; padding: 0.75rem; border: 1px solid #252525; background-color: #111111; border-radius: 14px; color: #c0c0c0; font-size: 1.1rem; text-align: center; font-weight: 500; text-transform: uppercase; }
|
|
303
|
-
.input-key-style:focus { border-color: #505050; outline: none; box-shadow: 0 0 0 2px rgba(80, 80, 80, 0.5); transform: scale(1.1); }
|
|
304
|
-
.settings-box { border: 1px solid #333; border-radius: 24px; background-color: #000000; padding: 1.5rem; }
|
|
305
|
-
.nav-tab { padding: 0.5rem 1rem; color: var(--tab-text); font-size: 0.875rem; font-weight: 400; border-radius: 12px; text-decoration: none; display: flex; align-items: center; gap: 0.5rem; border: 1px solid transparent; cursor: pointer; background: transparent; }
|
|
306
|
-
.nav-tab:hover { color: var(--tab-hover-text); background-color: rgba(79, 70, 229, 0.05); border-color: #4f46e5; transform: scale(1.05); }
|
|
307
|
-
.nav-tab.active { color: var(--tab-active-text); border-color: var(--tab-active-border); background-color: var(--tab-active-bg); transform: scale(1.05); }
|
|
308
|
-
.eagler-dropdown { position: relative; background-color: rgba(20, 20, 20, 0.98); border: 1px solid rgba(255, 255, 255, 0.15); border-radius: 14px; padding: 0.5rem; min-width: 200px; display: inline-block; }
|
|
309
|
-
.eagler-dropdown-link { display: block; padding: 0.6rem 0.8rem; color: #d1d5db; text-decoration: none; border-radius: 14px; font-size: 0.9rem; transition: all 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275); }
|
|
310
|
-
.eagler-dropdown-link:hover { background-color: rgba(255, 255, 255, 0.1); color: white; transform: translateX(4px); }
|
|
311
|
-
#notification-container { position: fixed; bottom: 2rem; right: 2rem; display: flex; flex-direction: column; gap: 0.75rem; z-index: 1000; pointer-events: none; }
|
|
312
|
-
.notification-toast { background-color: #0a0a0a; border: 1px solid #333; border-radius: 14px; padding: 0.75rem 1.25rem; color: #fff; box-shadow: 0 4px 15px rgba(0,0,0,0.5); display: flex; align-items: center; gap: 0.75rem; font-size: 0.9rem; min-width: 200px; transform: translateX(120%); transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.3s ease, background-color 0.2s; opacity: 0; pointer-events: auto; cursor: default; }
|
|
313
|
-
.notification-toast.show { transform: translateX(0); opacity: 1; }
|
|
314
|
-
.notification-toast.show:hover { transform: scale(1.05) translateX(-5px); background-color: #151515; border-color: #555; box-shadow: 0 8px 25px rgba(0,0,0,0.7); }
|
|
315
|
-
.notification-icon { font-size: 1.1rem; }
|
|
316
|
-
.notification-icon.success { color: #4ade80; }
|
|
317
|
-
.notification-icon.info { color: #60a5fa; }
|
|
318
|
-
.notification-icon.warning { color: #fbbf24; }
|
|
319
|
-
|
|
320
|
-
</style>
|
|
270
|
+
|
|
271
|
+
/* Option Styling (Limited support) */
|
|
272
|
+
option { background-color: #000; color: #fff; padding: 10px; }
|
|
273
|
+
|
|
274
|
+
/* INJECTED STYLES (Animations) */
|
|
275
|
+
:root { --bg-page: #040404; --bg-container: #000000; --border-color: #333; --text-primary: #c0c0c0; --text-light-grey: #d1d5db; --accent-color: #6366f1; }
|
|
276
|
+
@keyframes shake { 0% { transform: translateX(0); } 25% { transform: translateX(-5px) rotate(-5deg); } 50% { transform: translateX(5px) rotate(5deg); } 75% { transform: translateX(-5px) rotate(-5deg); } 100% { transform: translateX(0); } }
|
|
277
|
+
.shake-anim { animation: shake 0.4s cubic-bezier(.36,.07,.19,.97) both; }
|
|
278
|
+
body:not(.no-animations) .btn-toolbar-style, body:not(.no-animations) .hourly-item, body:not(.no-animations) .daily-row { transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); }
|
|
279
|
+
body:not(.no-animations) .btn-toolbar-style:active { transform: scale(0.96); }
|
|
280
|
+
</style>
|
|
321
281
|
|
|
322
282
|
</head>
|
|
323
|
-
<body class="min-h-screen
|
|
283
|
+
<body class="min-h-screen">
|
|
324
284
|
|
|
325
|
-
<!-- LOADING OVERLAY -->
|
|
326
285
|
<div id="loading-overlay">
|
|
327
|
-
<
|
|
286
|
+
<i class="fa-solid fa-spinner fa-spin fa-2x text-white mb-4"></i>
|
|
328
287
|
<div id="loading-text" class="text-sm tracking-widest uppercase text-gray-500">Locating...</div>
|
|
329
288
|
</div>
|
|
330
289
|
|
|
331
|
-
<!-- HOURLY DETAIL MODAL -->
|
|
332
290
|
<div id="hourly-modal-overlay">
|
|
333
291
|
<div class="modal-content">
|
|
334
292
|
<div class="flex items-center gap-4">
|
|
@@ -338,54 +296,41 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
338
296
|
<div id="modal-desc" class="text-gray-400 text-sm"></div>
|
|
339
297
|
</div>
|
|
340
298
|
</div>
|
|
341
|
-
<div id="modal-details-list" class="flex flex-col gap-3">
|
|
342
|
-
|
|
343
|
-
</
|
|
344
|
-
<button class="close-modal-btn" onclick="closeHourlyModal()">Close</button>
|
|
299
|
+
<div id="modal-details-list" class="flex flex-col gap-3 pt-2">
|
|
300
|
+
</div>
|
|
301
|
+
<button class="btn-toolbar-style w-full mt-4 justify-center" onclick="closeHourlyModal()">Close</button>
|
|
345
302
|
</div>
|
|
346
303
|
</div>
|
|
347
304
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
<div class="w-full md:w-auto">
|
|
305
|
+
<header>
|
|
306
|
+
<div>
|
|
351
307
|
<h1 class="text-2xl font-bold text-white tracking-wide">4SP WEATHER</h1>
|
|
352
308
|
<div id="location-display" class="text-xs text-[#707070] flex items-center gap-2 mt-1">
|
|
353
|
-
|
|
309
|
+
<span id="loc-name">Detecting...</span>
|
|
354
310
|
</div>
|
|
355
311
|
</div>
|
|
356
312
|
|
|
357
|
-
<div class="flex items-center gap-4 w-full md:w-auto
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
<
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
<!-- Injected via JS -->
|
|
368
|
-
</div>
|
|
369
|
-
</div>
|
|
370
|
-
|
|
371
|
-
<!-- API Provider Select -->
|
|
372
|
-
<div class="relative">
|
|
373
|
-
<select id="provider-select" class="btn-toolbar appearance-none pr-8 outline-none focus:border-indigo-500">
|
|
374
|
-
<option value="nws">🇺🇸 NWS (USA)</option>
|
|
375
|
-
<option value="open-meteo">🌍 Open-Meteo</option>
|
|
313
|
+
<div class="flex flex-col md:flex-row items-center gap-4 w-full md:w-auto">
|
|
314
|
+
<button id="btn-geo" class="btn-toolbar-style w-full md:w-auto">
|
|
315
|
+
<i class="fa-solid fa-location-crosshairs text-[#6366f1]"></i>
|
|
316
|
+
<span>Use Current Location</span>
|
|
317
|
+
</button>
|
|
318
|
+
|
|
319
|
+
<div class="provider-select-wrapper w-full md:w-auto">
|
|
320
|
+
<select id="provider-select" class="provider-select">
|
|
321
|
+
<option value="nws">🇺🇸 NWS (Official)</option>
|
|
322
|
+
<option value="open-meteo">🌍 Open-Meteo (Global)</option>
|
|
376
323
|
</select>
|
|
377
|
-
<i class="fa-solid fa-chevron-down
|
|
324
|
+
<i class="fa-solid fa-chevron-down select-icon"></i>
|
|
378
325
|
</div>
|
|
379
326
|
</div>
|
|
380
327
|
</header>
|
|
381
328
|
|
|
382
|
-
<!-- MAIN CONTENT -->
|
|
383
329
|
<main id="main-content" class="flex-1 p-4 md:p-8 max-w-5xl mx-auto w-full flex flex-col gap-6 opacity-0 transition-opacity duration-500">
|
|
384
330
|
|
|
385
|
-
<!-- CURRENT CONDITIONS HERO -->
|
|
386
331
|
<div class="weather-card flex flex-col md:flex-row items-center justify-between gap-8">
|
|
387
|
-
<div class="flex flex-col items-center md:items-start">
|
|
388
|
-
<div class="text-
|
|
332
|
+
<div class="flex flex-col items-center md:items-start text-center md:text-left">
|
|
333
|
+
<div class="text-xs text-[#4f46e5] uppercase tracking-widest font-bold mb-2">Current Conditions</div>
|
|
389
334
|
<div class="flex items-center gap-6">
|
|
390
335
|
<div id="curr-icon" class="current-icon"><i class="fa-solid fa-spinner fa-spin"></i></div>
|
|
391
336
|
<div>
|
|
@@ -395,40 +340,38 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
395
340
|
</div>
|
|
396
341
|
</div>
|
|
397
342
|
|
|
398
|
-
<div class="grid grid-cols-2 gap-x-12 gap-y-
|
|
399
|
-
<div>
|
|
400
|
-
<div class="text-
|
|
343
|
+
<div class="grid grid-cols-2 gap-x-12 gap-y-6 text-right w-full md:w-auto">
|
|
344
|
+
<div class="flex flex-col">
|
|
345
|
+
<div class="text-[10px] text-[#707070] uppercase tracking-wider mb-1">High</div>
|
|
401
346
|
<div id="curr-high" class="text-2xl text-white font-medium">--°</div>
|
|
402
347
|
</div>
|
|
403
|
-
<div>
|
|
404
|
-
<div class="text-
|
|
348
|
+
<div class="flex flex-col">
|
|
349
|
+
<div class="text-[10px] text-[#707070] uppercase tracking-wider mb-1">Low</div>
|
|
405
350
|
<div id="curr-low" class="text-2xl text-gray-400 font-medium">--°</div>
|
|
406
351
|
</div>
|
|
407
|
-
<div>
|
|
408
|
-
<div class="text-
|
|
352
|
+
<div class="flex flex-col">
|
|
353
|
+
<div class="text-[10px] text-[#707070] uppercase tracking-wider mb-1">Wind</div>
|
|
409
354
|
<div id="curr-wind" class="text-lg text-gray-300">--</div>
|
|
410
355
|
</div>
|
|
411
|
-
<div>
|
|
412
|
-
<div class="text-
|
|
356
|
+
<div class="flex flex-col">
|
|
357
|
+
<div class="text-[10px] text-[#707070] uppercase tracking-wider mb-1">Humidity</div>
|
|
413
358
|
<div id="curr-hum" class="text-lg text-gray-300">--%</div>
|
|
414
359
|
</div>
|
|
415
360
|
</div>
|
|
416
361
|
</div>
|
|
417
362
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
<div class="
|
|
421
|
-
|
|
422
|
-
|
|
363
|
+
<div class="weather-card overflow-hidden">
|
|
364
|
+
<div class="text-xs text-gray-500 uppercase tracking-widest mb-2 px-2">Hourly Forecast</div>
|
|
365
|
+
<div class="scroll-mask-container">
|
|
366
|
+
<div id="hourly-container" class="hourly-container">
|
|
367
|
+
</div>
|
|
423
368
|
</div>
|
|
424
369
|
</div>
|
|
425
370
|
|
|
426
|
-
<!-- DAILY FORECAST -->
|
|
427
371
|
<div class="weather-card">
|
|
428
|
-
<div class="text-
|
|
372
|
+
<div class="text-xs text-gray-500 uppercase tracking-widest mb-4">7-Day Forecast</div>
|
|
429
373
|
<div id="daily-container">
|
|
430
|
-
|
|
431
|
-
</div>
|
|
374
|
+
</div>
|
|
432
375
|
</div>
|
|
433
376
|
|
|
434
377
|
</main>
|
|
@@ -442,7 +385,7 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
442
385
|
region: null,
|
|
443
386
|
provider: 'nws',
|
|
444
387
|
data: null,
|
|
445
|
-
locationAllowed: false
|
|
388
|
+
locationAllowed: false
|
|
446
389
|
};
|
|
447
390
|
|
|
448
391
|
// --- DOM ELEMENTS ---
|
|
@@ -451,10 +394,6 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
451
394
|
const mainContent = document.getElementById('main-content');
|
|
452
395
|
const locNameEl = document.getElementById('loc-name');
|
|
453
396
|
const providerSelect = document.getElementById('provider-select');
|
|
454
|
-
|
|
455
|
-
// Search Elements
|
|
456
|
-
const searchInput = document.getElementById('loc-search-input');
|
|
457
|
-
const searchResults = document.getElementById('search-results');
|
|
458
397
|
const btnGeo = document.getElementById('btn-geo');
|
|
459
398
|
|
|
460
399
|
// Current Els
|
|
@@ -477,10 +416,9 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
477
416
|
const modalIconCont = document.getElementById('modal-icon-container');
|
|
478
417
|
const modalList = document.getElementById('modal-details-list');
|
|
479
418
|
|
|
480
|
-
// --- ICONS MAPPING
|
|
419
|
+
// --- ICONS MAPPING ---
|
|
481
420
|
function getIconFromWMO(code, isDay = 1) {
|
|
482
421
|
const day = isDay === 1;
|
|
483
|
-
// Mapped to FA 6 Free Icons
|
|
484
422
|
const map = {
|
|
485
423
|
0: day ? 'fa-sun' : 'fa-moon',
|
|
486
424
|
1: day ? 'fa-cloud-sun' : 'fa-cloud-moon',
|
|
@@ -498,7 +436,7 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
498
436
|
|
|
499
437
|
function getIconFromNWS(shortForecast, isDay) {
|
|
500
438
|
const lower = shortForecast.toLowerCase();
|
|
501
|
-
const prefix = 'fa-solid';
|
|
439
|
+
const prefix = 'fa-solid';
|
|
502
440
|
|
|
503
441
|
if (lower.includes('thunder')) return `${prefix} fa-cloud-bolt`;
|
|
504
442
|
if (lower.includes('snow') || lower.includes('flurries')) return `${prefix} fa-snowflake`;
|
|
@@ -543,7 +481,7 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
543
481
|
loadWeather();
|
|
544
482
|
} catch (err) {
|
|
545
483
|
loadingText.innerText = "Location Failed.";
|
|
546
|
-
alert("Could not determine location.
|
|
484
|
+
alert("Could not determine location. Check browser permissions.");
|
|
547
485
|
loadingOverlay.style.opacity = '0';
|
|
548
486
|
loadingOverlay.style.pointerEvents = 'none';
|
|
549
487
|
}
|
|
@@ -571,7 +509,7 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
571
509
|
updateLocationDisplay();
|
|
572
510
|
} catch (e) {
|
|
573
511
|
console.warn("IP location fetch failed:", e);
|
|
574
|
-
// Fallback to defaults
|
|
512
|
+
// Fallback to defaults (NY)
|
|
575
513
|
STATE.lat = 40.7128;
|
|
576
514
|
STATE.lon = -74.0060;
|
|
577
515
|
STATE.city = "New York";
|
|
@@ -580,129 +518,10 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
580
518
|
}
|
|
581
519
|
}
|
|
582
520
|
|
|
583
|
-
// --- SEARCH LOGIC ---
|
|
584
|
-
let debounceTimer;
|
|
585
|
-
searchInput.addEventListener('input', (e) => {
|
|
586
|
-
clearTimeout(debounceTimer);
|
|
587
|
-
const query = e.target.value.trim();
|
|
588
|
-
if(query.length < 3) {
|
|
589
|
-
searchResults.classList.remove('active');
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
592
|
-
debounceTimer = setTimeout(() => fetchSearchResults(query), 500);
|
|
593
|
-
});
|
|
594
|
-
|
|
595
|
-
document.addEventListener('click', (e) => {
|
|
596
|
-
if(!searchInput.contains(e.target) && !searchResults.contains(e.target)) {
|
|
597
|
-
searchResults.classList.remove('active');
|
|
598
|
-
}
|
|
599
|
-
});
|
|
600
|
-
|
|
601
521
|
btnGeo.addEventListener('click', () => {
|
|
602
|
-
searchInput.value = '';
|
|
603
522
|
triggerAutoLocation();
|
|
604
523
|
});
|
|
605
524
|
|
|
606
|
-
async function fetchSearchResults(query) {
|
|
607
|
-
let viewbox = '';
|
|
608
|
-
if (STATE.lat && STATE.lon) {
|
|
609
|
-
const b = 1;
|
|
610
|
-
viewbox = `&viewbox=${STATE.lon-b},${STATE.lat+b},${STATE.lon+b},${STATE.lat-b}`;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
try {
|
|
614
|
-
// Request more results to filter locally
|
|
615
|
-
const url = `https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(query)}&limit=20&addressdetails=1${viewbox}`;
|
|
616
|
-
const res = await fetch(url);
|
|
617
|
-
const data = await res.json();
|
|
618
|
-
|
|
619
|
-
// Filter for only Places (cities, towns, villages) or Administrative boundaries
|
|
620
|
-
const filtered = data.filter(item => {
|
|
621
|
-
const type = item.type;
|
|
622
|
-
const category = item.class;
|
|
623
|
-
const validClasses = ['place', 'boundary'];
|
|
624
|
-
const validTypes = ['city', 'town', 'village', 'hamlet', 'administrative'];
|
|
625
|
-
|
|
626
|
-
if (!validClasses.includes(category)) return false;
|
|
627
|
-
if (category === 'boundary' && type !== 'administrative') return false;
|
|
628
|
-
|
|
629
|
-
return true;
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
renderSearchResults(filtered.slice(0, 5)); // Show top 5 matches
|
|
633
|
-
} catch (e) {
|
|
634
|
-
console.error("Search failed", e);
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
function renderSearchResults(results) {
|
|
639
|
-
searchResults.innerHTML = '';
|
|
640
|
-
|
|
641
|
-
if (results.length === 0) {
|
|
642
|
-
const empty = document.createElement('div');
|
|
643
|
-
empty.className = 'search-item text-gray-500 cursor-default';
|
|
644
|
-
empty.innerText = 'No places found';
|
|
645
|
-
searchResults.appendChild(empty);
|
|
646
|
-
} else {
|
|
647
|
-
results.forEach(loc => {
|
|
648
|
-
const div = document.createElement('div');
|
|
649
|
-
div.className = 'search-item';
|
|
650
|
-
|
|
651
|
-
const address = loc.address || {};
|
|
652
|
-
const city = address.city || address.town || address.village || address.hamlet || loc.name;
|
|
653
|
-
const state = address.state || address.country;
|
|
654
|
-
|
|
655
|
-
div.innerHTML = `
|
|
656
|
-
<div class="search-item-main">${city}</div>
|
|
657
|
-
<div class="search-item-sub">${state}</div>
|
|
658
|
-
`;
|
|
659
|
-
|
|
660
|
-
div.addEventListener('click', () => {
|
|
661
|
-
selectLocation(parseFloat(loc.lat), parseFloat(loc.lon), city, state, address.country_code);
|
|
662
|
-
});
|
|
663
|
-
|
|
664
|
-
searchResults.appendChild(div);
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
// Add "Use Current Location" at the bottom
|
|
669
|
-
const currDiv = document.createElement('div');
|
|
670
|
-
currDiv.className = 'search-item border-t border-[#333] mt-1 pt-2';
|
|
671
|
-
if (STATE.locationAllowed) {
|
|
672
|
-
currDiv.innerHTML = `<div class="search-item-main text-[#6366f1]"><i class="fa-solid fa-location-crosshairs mr-2"></i> Use Current Location</div>`;
|
|
673
|
-
currDiv.addEventListener('click', () => {
|
|
674
|
-
searchInput.value = '';
|
|
675
|
-
searchResults.classList.remove('active');
|
|
676
|
-
triggerAutoLocation();
|
|
677
|
-
});
|
|
678
|
-
} else {
|
|
679
|
-
currDiv.innerHTML = `<div class="search-item-main text-gray-500 cursor-not-allowed"><i class="fa-solid fa-location-slash mr-2"></i> Current Location Unavailable</div>`;
|
|
680
|
-
}
|
|
681
|
-
searchResults.appendChild(currDiv);
|
|
682
|
-
|
|
683
|
-
searchResults.classList.add('active');
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
function selectLocation(lat, lon, city, region, countryCode) {
|
|
687
|
-
STATE.lat = lat;
|
|
688
|
-
STATE.lon = lon;
|
|
689
|
-
STATE.city = city;
|
|
690
|
-
STATE.region = region;
|
|
691
|
-
|
|
692
|
-
searchInput.value = '';
|
|
693
|
-
searchResults.classList.remove('active');
|
|
694
|
-
|
|
695
|
-
const isUS = (countryCode && countryCode === 'us');
|
|
696
|
-
|
|
697
|
-
if (!isUS) {
|
|
698
|
-
STATE.provider = 'open-meteo';
|
|
699
|
-
providerSelect.value = 'open-meteo';
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
updateLocationDisplay();
|
|
703
|
-
loadWeather();
|
|
704
|
-
}
|
|
705
|
-
|
|
706
525
|
// --- WEATHER FETCHING LOGIC ---
|
|
707
526
|
|
|
708
527
|
async function loadWeather() {
|
|
@@ -739,7 +558,7 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
739
558
|
}
|
|
740
559
|
}
|
|
741
560
|
|
|
742
|
-
// --- NWS
|
|
561
|
+
// --- NWS API ---
|
|
743
562
|
async function fetchNWS() {
|
|
744
563
|
loadingText.innerText = "Contacting National Weather Service...";
|
|
745
564
|
const pointsUrl = `https://api.weather.gov/points/${STATE.lat},${STATE.lon}`;
|
|
@@ -767,18 +586,15 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
767
586
|
|
|
768
587
|
function processNWSData(dailyRaw, hourlyRaw) {
|
|
769
588
|
const current = hourlyRaw.properties.periods[0];
|
|
770
|
-
|
|
771
589
|
const periodGroups = {};
|
|
772
590
|
|
|
773
591
|
dailyRaw.properties.periods.forEach(p => {
|
|
774
592
|
const date = p.startTime.split('T')[0];
|
|
775
593
|
if (!periodGroups[date]) {
|
|
776
|
-
periodGroups[date] = { temps: [], icons: [],
|
|
594
|
+
periodGroups[date] = { temps: [], icons: [], isDay: [] };
|
|
777
595
|
}
|
|
778
596
|
periodGroups[date].temps.push(p.temperature);
|
|
779
597
|
periodGroups[date].icons.push(p.shortForecast);
|
|
780
|
-
periodGroups[date].precips.push(p.probabilityOfPrecipitation.value || 0);
|
|
781
|
-
periodGroups[date].winds.push(parseInt(p.windSpeed) || 0);
|
|
782
598
|
periodGroups[date].isDay.push(p.isDaytime);
|
|
783
599
|
});
|
|
784
600
|
|
|
@@ -786,7 +602,6 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
786
602
|
const group = periodGroups[date];
|
|
787
603
|
const high = Math.max(...group.temps);
|
|
788
604
|
const low = Math.min(...group.temps);
|
|
789
|
-
|
|
790
605
|
let iconDesc = group.icons[0];
|
|
791
606
|
const dayIdx = group.isDay.indexOf(true);
|
|
792
607
|
if (dayIdx !== -1) iconDesc = group.icons[dayIdx];
|
|
@@ -807,8 +622,7 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
807
622
|
low: dailyList[0]?.low || current.temperature - 10,
|
|
808
623
|
wind: `${current.windSpeed} ${current.windDirection}`,
|
|
809
624
|
humidity: current.relativeHumidity.value,
|
|
810
|
-
iconClass: getIconFromNWS(current.shortForecast, current.isDaytime)
|
|
811
|
-
isNWS: true
|
|
625
|
+
iconClass: getIconFromNWS(current.shortForecast, current.isDaytime)
|
|
812
626
|
},
|
|
813
627
|
hourly: hourlyRaw.properties.periods.slice(0, 24).map(h => ({
|
|
814
628
|
time: new Date(h.startTime).getHours(),
|
|
@@ -826,7 +640,7 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
826
640
|
};
|
|
827
641
|
}
|
|
828
642
|
|
|
829
|
-
// --- OPEN-METEO ---
|
|
643
|
+
// --- OPEN-METEO API ---
|
|
830
644
|
async function fetchOpenMeteo() {
|
|
831
645
|
loadingText.innerText = "Contacting Open-Meteo...";
|
|
832
646
|
if (!STATE.city) updateLocationDisplay();
|
|
@@ -842,7 +656,6 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
842
656
|
const curr = data.current;
|
|
843
657
|
const daily = data.daily;
|
|
844
658
|
const hourly = data.hourly;
|
|
845
|
-
|
|
846
659
|
const now = new Date();
|
|
847
660
|
const currentHour = now.getHours();
|
|
848
661
|
|
|
@@ -850,13 +663,11 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
850
663
|
for(let i = currentHour; i < currentHour + 24; i++) {
|
|
851
664
|
if (hourly.time[i]) {
|
|
852
665
|
const d = new Date(hourly.time[i]);
|
|
853
|
-
const temp = Math.round(hourly.temperature_2m[i]);
|
|
854
666
|
const code = hourly.weather_code[i];
|
|
855
|
-
|
|
856
667
|
hourlyList.push({
|
|
857
668
|
time: d.getHours(),
|
|
858
669
|
fullTime: hourly.time[i],
|
|
859
|
-
temp:
|
|
670
|
+
temp: Math.round(hourly.temperature_2m[i]),
|
|
860
671
|
desc: getWMODescription(code),
|
|
861
672
|
iconClass: getIconFromWMO(code, hourly.is_day[i]),
|
|
862
673
|
details: {
|
|
@@ -888,8 +699,7 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
888
699
|
low: Math.round(daily.temperature_2m_min[0]),
|
|
889
700
|
wind: `${Math.round(curr.wind_speed_10m)} mph`,
|
|
890
701
|
humidity: curr.relative_humidity_2m,
|
|
891
|
-
iconClass: getIconFromWMO(curr.weather_code, curr.is_day)
|
|
892
|
-
isNWS: false
|
|
702
|
+
iconClass: getIconFromWMO(curr.weather_code, curr.is_day)
|
|
893
703
|
},
|
|
894
704
|
hourly: hourlyList,
|
|
895
705
|
daily: dailyList
|
|
@@ -936,8 +746,8 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
936
746
|
<div class="daily-day">${day.dateName}</div>
|
|
937
747
|
<div class="daily-icon text-xl"><i class="${day.iconClass}"></i></div>
|
|
938
748
|
<div class="daily-temps">
|
|
939
|
-
<span class="temp-high">${day.high}°</span>
|
|
940
|
-
<span class="temp-low">${day.low}°</span>
|
|
749
|
+
<span class="temp-high font-medium">${day.high}°</span>
|
|
750
|
+
<span class="temp-low text-sm">${day.low}°</span>
|
|
941
751
|
</div>
|
|
942
752
|
</div>
|
|
943
753
|
`).join('');
|
|
@@ -1037,4 +847,4 @@ body:not(.no-animations) .btn-toolbar-style:active, body:not(.no-animations) .bt
|
|
|
1037
847
|
</script>
|
|
1038
848
|
|
|
1039
849
|
</body>
|
|
1040
|
-
</html>
|
|
850
|
+
</html>
|