class-studys 6.0.0 → 7.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.html +708 -2102
- package/package.json +2 -2
package/index.html
CHANGED
|
@@ -3,2149 +3,755 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>IGNITE OS
|
|
7
|
-
<meta name="description" content="IGNITE OS: games, movies from Drive and a real web tunnel. Dark, orange, clean.">
|
|
8
|
-
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='14' fill='%230d0906'/%3E%3Ccircle cx='32' cy='32' r='17' fill='%23ff6b1a'/%3E%3Ccircle cx='26' cy='26' r='5' fill='%230d0906'/%3E%3C/svg%3E">
|
|
9
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
|
-
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800;900&family=Space+Grotesk:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
|
|
6
|
+
<title>IGNITE OS</title>
|
|
12
7
|
<style>
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
--
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
::
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
animation
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
.
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
background:
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
.
|
|
168
|
-
.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
.
|
|
173
|
-
.
|
|
174
|
-
|
|
175
|
-
.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
box-shadow: 0 0
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
.
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
.
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
.
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
.
|
|
246
|
-
.
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
.
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
272
|
-
.
|
|
273
|
-
.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
.
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
transition:
|
|
285
|
-
}
|
|
286
|
-
.
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
background:
|
|
294
|
-
border-radius:
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
.tunnel-input:focus, .search-input:focus {
|
|
298
|
-
border-color: var(--accent);
|
|
299
|
-
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
|
|
300
|
-
}
|
|
301
|
-
.chips { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 16px; }
|
|
302
|
-
.chip {
|
|
303
|
-
background: rgba(255, 255, 255, 0.045); border: 1px solid var(--line);
|
|
304
|
-
color: var(--muted); border-radius: 99px; padding: 7px 15px;
|
|
305
|
-
font-size: 0.82rem; font-weight: 600; transition: 0.22s var(--ease);
|
|
306
|
-
}
|
|
307
|
-
.chip:hover {
|
|
308
|
-
color: var(--text); border-color: var(--accent);
|
|
309
|
-
background: color-mix(in srgb, var(--accent) 10%, transparent);
|
|
310
|
-
transform: translateY(-1px);
|
|
311
|
-
}
|
|
312
|
-
.tunnel-tip { margin-top: 16px; font-size: 0.78rem; color: var(--dim); }
|
|
313
|
-
|
|
314
|
-
.local-hint {
|
|
315
|
-
background: color-mix(in srgb, var(--accent) 8%, transparent);
|
|
316
|
-
border: 1px dashed var(--line-2); border-radius: 12px;
|
|
317
|
-
padding: 12px 16px; font-size: 0.85rem; color: var(--muted); margin-bottom: 16px;
|
|
318
|
-
}
|
|
319
|
-
/* ─────────── game cards ─────────── */
|
|
320
|
-
|
|
321
|
-
.game-grid {
|
|
322
|
-
display: grid; gap: 16px;
|
|
323
|
-
grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
|
|
324
|
-
}
|
|
325
|
-
.game-card {
|
|
326
|
-
position: relative; border-radius: var(--radius); overflow: hidden;
|
|
327
|
-
background: var(--panel-2); border: 1px solid var(--line);
|
|
328
|
-
transition: 0.3s var(--ease); cursor: pointer;
|
|
329
|
-
}
|
|
330
|
-
.game-card:hover {
|
|
331
|
-
transform: translateY(-5px); border-color: var(--accent);
|
|
332
|
-
box-shadow: 0 16px 44px rgba(0, 0, 0, 0.5), 0 0 30px color-mix(in srgb, var(--accent) 18%, transparent);
|
|
333
|
-
}
|
|
334
|
-
.game-cover {
|
|
335
|
-
position: relative; height: 130px; overflow: hidden;
|
|
336
|
-
background:
|
|
337
|
-
radial-gradient(120% 130% at 12% 8%, color-mix(in srgb, var(--gcolor) 45%, transparent), transparent 55%),
|
|
338
|
-
radial-gradient(120% 130% at 90% 95%, color-mix(in srgb, var(--accent2) 30%, transparent), transparent 60%),
|
|
339
|
-
linear-gradient(150deg, #241a12, #120a06 70%);
|
|
340
|
-
}
|
|
341
|
-
.game-cover img {
|
|
342
|
-
position: absolute; inset: 0; width: 100%; height: 100%;
|
|
343
|
-
object-fit: cover; filter: saturate(1.1); transition: 0.5s var(--ease);
|
|
344
|
-
z-index: 1;
|
|
345
|
-
}
|
|
346
|
-
.game-card:hover .game-cover img { transform: scale(1.06); }
|
|
347
|
-
.cover-fallback {
|
|
348
|
-
position: absolute; inset: 0; display: grid; place-items: center;
|
|
349
|
-
font-size: 3rem; font-weight: 900;
|
|
350
|
-
color: color-mix(in srgb, var(--gcolor) 70%, #fff);
|
|
351
|
-
text-shadow: 0 0 30px color-mix(in srgb, var(--gcolor) 60%, transparent);
|
|
352
|
-
}
|
|
353
|
-
.game-play {
|
|
354
|
-
position: absolute; right: 12px; bottom: -18px;
|
|
355
|
-
width: 44px; height: 44px; border-radius: 50%;
|
|
356
|
-
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
|
357
|
-
color: var(--on-accent); display: grid; place-items: center;
|
|
358
|
-
font-size: 1rem; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
|
|
359
|
-
transition: 0.25s var(--ease); border: none;
|
|
360
|
-
}
|
|
361
|
-
.game-card:hover .game-play { bottom: 14px; }
|
|
362
|
-
.game-body { padding: 16px 16px 18px; }
|
|
363
|
-
.game-name { font-weight: 800; font-size: 1.02rem; display: flex; align-items: center; gap: 8px; }
|
|
364
|
-
.game-tags { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
|
|
365
|
-
.game-tag {
|
|
366
|
-
font-size: 0.66rem; letter-spacing: 1px; text-transform: uppercase;
|
|
367
|
-
padding: 3px 9px; border-radius: 99px; color: var(--muted);
|
|
368
|
-
background: rgba(255, 255, 255, 0.05); border: 1px solid var(--line);
|
|
369
|
-
}
|
|
370
|
-
.game-desc {
|
|
371
|
-
color: var(--muted); font-size: 0.82rem; margin-top: 8px;
|
|
372
|
-
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
|
|
373
|
-
}
|
|
374
|
-
.game-tools {
|
|
375
|
-
position: absolute; top: 10px; left: 10px; display: flex; gap: 6px;
|
|
376
|
-
opacity: 0; transition: 0.2s var(--ease); z-index: 3;
|
|
377
|
-
}
|
|
378
|
-
.game-card:hover .game-tools { opacity: 1; }
|
|
379
|
-
.game-tools .icon-btn { width: 30px; height: 30px; font-size: 0.72rem; background: rgba(0, 0, 0, 0.55); }
|
|
380
|
-
.local-dot {
|
|
381
|
-
position: absolute; top: 10px; right: 10px;
|
|
382
|
-
background: color-mix(in srgb, var(--gcolor) 85%, #000); color: #fff;
|
|
383
|
-
font-size: 0.6rem; font-weight: 800; letter-spacing: 1px;
|
|
384
|
-
padding: 3px 8px; border-radius: 99px;
|
|
385
|
-
}
|
|
386
|
-
/* ─────────── movies ─────────── */
|
|
387
|
-
|
|
388
|
-
.movie-toolbar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 18px; }
|
|
389
|
-
.movie-toolbar .search-input { flex: 0 1 300px; }
|
|
390
|
-
.genre-chips { display: flex; gap: 6px; flex-wrap: wrap; flex: 1; }
|
|
391
|
-
.genre-chip {
|
|
392
|
-
background: rgba(255, 255, 255, 0.045); border: 1px solid var(--line);
|
|
393
|
-
color: var(--muted); border-radius: 99px; padding: 5px 12px;
|
|
394
|
-
font-size: 0.74rem; font-weight: 700; transition: 0.2s var(--ease);
|
|
395
|
-
}
|
|
396
|
-
.genre-chip.active, .genre-chip:hover {
|
|
397
|
-
color: var(--text); border-color: var(--accent);
|
|
398
|
-
background: color-mix(in srgb, var(--accent) 12%, transparent);
|
|
399
|
-
}
|
|
400
|
-
.select-mini {
|
|
401
|
-
background: rgba(0, 0, 0, 0.32); border: 1px solid var(--line);
|
|
402
|
-
color: var(--text); border-radius: 10px; padding: 9px 12px;
|
|
403
|
-
font-size: 0.82rem; outline: none;
|
|
404
|
-
}
|
|
405
|
-
.select-mini:focus { border-color: var(--accent); }
|
|
406
|
-
|
|
407
|
-
.movie-grid {
|
|
408
|
-
display: grid; gap: 16px;
|
|
409
|
-
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
|
410
|
-
}
|
|
411
|
-
.movie-card {
|
|
412
|
-
position: relative; border-radius: var(--radius); overflow: hidden;
|
|
413
|
-
background: var(--panel-2); border: 1px solid var(--line);
|
|
414
|
-
transition: 0.3s var(--ease); cursor: pointer;
|
|
415
|
-
}
|
|
416
|
-
.movie-card:hover {
|
|
417
|
-
transform: translateY(-5px); border-color: var(--accent);
|
|
418
|
-
box-shadow: 0 14px 40px rgba(0, 0, 0, 0.5), 0 0 26px color-mix(in srgb, var(--accent) 15%, transparent);
|
|
419
|
-
}
|
|
420
|
-
.movie-poster {
|
|
421
|
-
position: relative; aspect-ratio: 2 / 3; overflow: hidden;
|
|
422
|
-
background:
|
|
423
|
-
radial-gradient(100% 120% at 50% 0%, color-mix(in srgb, var(--mcolor) 38%, transparent), transparent 60%),
|
|
424
|
-
linear-gradient(160deg, #241a12, #100a07);
|
|
425
|
-
display: grid; place-items: center;
|
|
426
|
-
}
|
|
427
|
-
.movie-poster img {
|
|
428
|
-
position: absolute; inset: 0; width: 100%; height: 100%;
|
|
429
|
-
object-fit: cover; transition: 0.5s var(--ease);
|
|
430
|
-
z-index: 1;
|
|
431
|
-
}
|
|
432
|
-
.movie-card:hover .movie-poster img { transform: scale(1.06); }
|
|
433
|
-
.movie-fallback {
|
|
434
|
-
font-size: 2.6rem; font-weight: 900;
|
|
435
|
-
color: color-mix(in srgb, var(--mcolor) 75%, #fff);
|
|
436
|
-
text-shadow: 0 0 28px color-mix(in srgb, var(--mcolor) 55%, transparent);
|
|
437
|
-
}
|
|
438
|
-
.movie-play {
|
|
439
|
-
position: absolute; inset: 0; display: grid; place-items: center;
|
|
440
|
-
background: rgba(0, 0, 0, 0.25); opacity: 0; transition: 0.25s var(--ease);
|
|
441
|
-
}
|
|
442
|
-
.movie-play i {
|
|
443
|
-
width: 52px; height: 52px; border-radius: 50%;
|
|
444
|
-
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
|
445
|
-
color: var(--on-accent); display: grid; place-items: center;
|
|
446
|
-
font-style: normal; font-size: 1.1rem;
|
|
447
|
-
box-shadow: 0 10px 30px color-mix(in srgb, var(--accent) 45%, transparent);
|
|
448
|
-
transform: scale(0.7); transition: 0.25s var(--ease);
|
|
449
|
-
}
|
|
450
|
-
.movie-card:hover .movie-play { opacity: 1; }
|
|
451
|
-
.movie-card:hover .movie-play i { transform: scale(1); }
|
|
452
|
-
.movie-info { padding: 12px 13px 14px; }
|
|
453
|
-
.movie-title { font-weight: 800; font-size: 0.92rem; line-height: 1.25; }
|
|
454
|
-
.movie-genre { font-size: 0.7rem; color: var(--muted); letter-spacing: 0.6px; margin-top: 3px; text-transform: uppercase; }
|
|
455
|
-
.movie-status { position: absolute; top: 9px; left: 9px; display: flex; gap: 5px; z-index: 2; }
|
|
456
|
-
.movie-status span {
|
|
457
|
-
background: rgba(0, 0, 0, 0.6); border: 1px solid rgba(255, 255, 255, 0.12);
|
|
458
|
-
color: #fff; font-size: 0.58rem; font-weight: 800; letter-spacing: 0.8px;
|
|
459
|
-
padding: 3px 8px; border-radius: 99px; backdrop-filter: blur(6px);
|
|
460
|
-
}
|
|
461
|
-
.movie-status .fav {
|
|
462
|
-
background: color-mix(in srgb, var(--accent) 75%, #000); border-color: transparent;
|
|
463
|
-
}
|
|
464
|
-
.movie-tools {
|
|
465
|
-
position: absolute; top: 9px; right: 9px; display: flex; gap: 5px;
|
|
466
|
-
opacity: 0; transition: 0.2s var(--ease); z-index: 3;
|
|
467
|
-
}
|
|
468
|
-
.movie-card:hover .movie-tools { opacity: 1; }
|
|
469
|
-
.movie-tools .icon-btn { width: 28px; height: 28px; font-size: 0.68rem; background: rgba(0, 0, 0, 0.55); }
|
|
470
|
-
.movie-cont-pct {
|
|
471
|
-
display: inline-block; margin-top: 6px; padding: 2px 7px;
|
|
472
|
-
font-size: 0.66rem; font-weight: 800; color: var(--accent2);
|
|
473
|
-
background: color-mix(in srgb, var(--accent) 14%, transparent);
|
|
474
|
-
border-radius: 99px;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
.empty-state {
|
|
478
|
-
text-align: center; padding: 46px 20px;
|
|
479
|
-
background: rgba(255, 255, 255, 0.02); border: 1px dashed var(--line-2);
|
|
480
|
-
border-radius: var(--radius); margin-top: 8px;
|
|
481
|
-
}
|
|
482
|
-
.empty-art { font-size: 3rem; margin-bottom: 10px; }
|
|
483
|
-
.empty-state h4 { font-size: 1.15rem; margin-bottom: 8px; }
|
|
484
|
-
.empty-state p { color: var(--muted); max-width: 44ch; margin: 0 auto 18px; font-size: 0.92rem; }
|
|
485
|
-
/* ─────────── boot splash ─────────── */
|
|
486
|
-
|
|
487
|
-
.boot {
|
|
488
|
-
position: fixed; inset: 0; z-index: 200;
|
|
489
|
-
background: radial-gradient(90% 90% at 50% 40%, #14100b, #070503 80%);
|
|
490
|
-
display: grid; place-items: center; transition: opacity 0.55s var(--ease);
|
|
491
|
-
}
|
|
492
|
-
.boot.done { opacity: 0; pointer-events: none; }
|
|
493
|
-
.boot-inner { text-align: center; padding: 20px; }
|
|
494
|
-
.boot-logo {
|
|
495
|
-
width: 84px; height: 84px; margin: 0 auto 22px;
|
|
496
|
-
border-radius: 22px; display: grid; place-items: center; font-size: 38px;
|
|
497
|
-
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
|
498
|
-
color: var(--on-accent);
|
|
499
|
-
box-shadow: 0 0 60px color-mix(in srgb, var(--accent) 55%, transparent);
|
|
500
|
-
animation: bootPulse 1.6s ease-in-out infinite;
|
|
501
|
-
}
|
|
502
|
-
@keyframes bootPulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.08); } }
|
|
503
|
-
.boot-title { font-size: 2rem; letter-spacing: 8px; margin-bottom: 16px; }
|
|
504
|
-
.boot-type {
|
|
505
|
-
font-family: 'JetBrains Mono', monospace; font-size: 0.8rem;
|
|
506
|
-
color: var(--accent2); min-height: 1.3em; margin-bottom: 22px;
|
|
507
|
-
}
|
|
508
|
-
.boot-bar {
|
|
509
|
-
width: 260px; height: 5px; border-radius: 99px; margin: 0 auto;
|
|
510
|
-
background: rgba(255, 255, 255, 0.08); overflow: hidden;
|
|
511
|
-
}
|
|
512
|
-
.boot-bar i {
|
|
513
|
-
display: block; height: 100%; width: 0%;
|
|
514
|
-
background: linear-gradient(90deg, var(--accent), var(--accent2));
|
|
515
|
-
border-radius: 99px; transition: width 0.25s ease;
|
|
516
|
-
box-shadow: 0 0 14px var(--accent);
|
|
517
|
-
}
|
|
518
|
-
#boot-skip { margin-top: 26px; }
|
|
519
|
-
|
|
520
|
-
/* ─────────── footer ─────────── */
|
|
521
|
-
|
|
522
|
-
.foot {
|
|
523
|
-
max-width: 1220px; margin: 0 auto; padding: 26px 22px 46px;
|
|
524
|
-
display: flex; justify-content: space-between; align-items: center; gap: 14px;
|
|
525
|
-
flex-wrap: wrap; color: var(--dim); font-size: 0.78rem;
|
|
526
|
-
border-top: 1px solid var(--line); letter-spacing: 0.6px;
|
|
527
|
-
}
|
|
528
|
-
.foot-hint kbd {
|
|
529
|
-
color: var(--accent2); border-color: var(--line-2); font-size: 0.72rem;
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
/* ─────────── customizer ─────────── */
|
|
533
|
-
|
|
534
|
-
.customizer {
|
|
535
|
-
position: fixed; top: 0; right: 0; bottom: 0; z-index: 120;
|
|
536
|
-
width: min(340px, 92vw);
|
|
537
|
-
background: rgba(16, 11, 8, 0.9);
|
|
538
|
-
backdrop-filter: blur(22px) saturate(1.4);
|
|
539
|
-
border-left: 1px solid var(--line);
|
|
540
|
-
transform: translateX(105%);
|
|
541
|
-
transition: transform 0.4s var(--ease);
|
|
542
|
-
display: flex; flex-direction: column;
|
|
543
|
-
box-shadow: -30px 0 80px rgba(0, 0, 0, 0.5);
|
|
544
|
-
}
|
|
545
|
-
.customizer.open { transform: translateX(0); }
|
|
546
|
-
.customizer-head {
|
|
547
|
-
display: flex; justify-content: space-between; align-items: center;
|
|
548
|
-
padding: 18px 20px; border-bottom: 1px solid var(--line);
|
|
549
|
-
}
|
|
550
|
-
.customizer-head h4 { font-size: 1.05rem; letter-spacing: 0.4px; }
|
|
551
|
-
.cz-scroll { padding: 20px; overflow-y: auto; display: flex; flex-direction: column; gap: 14px; }
|
|
552
|
-
.cz-label {
|
|
553
|
-
font-size: 0.66rem; letter-spacing: 2px; text-transform: uppercase;
|
|
554
|
-
color: var(--muted); font-weight: 700;
|
|
555
|
-
}
|
|
556
|
-
.swatches { display: flex; gap: 10px; flex-wrap: wrap; }
|
|
557
|
-
.swatch {
|
|
558
|
-
width: 38px; height: 38px; border-radius: 12px; border: 2px solid transparent;
|
|
559
|
-
background: var(--sw); box-shadow: 0 0 18px color-mix(in srgb, var(--sw) 45%, transparent);
|
|
560
|
-
transition: 0.2s var(--ease);
|
|
561
|
-
}
|
|
562
|
-
.swatch:hover { transform: scale(1.12); }
|
|
563
|
-
.swatch.active { border-color: #fff; transform: scale(1.12); }
|
|
564
|
-
.cz-row { display: flex; align-items: center; gap: 12px; }
|
|
565
|
-
input[type='color'] {
|
|
566
|
-
width: 46px; height: 34px; border: 1px solid var(--line); border-radius: 9px;
|
|
567
|
-
background: transparent; padding: 2px; cursor: pointer;
|
|
568
|
-
}
|
|
569
|
-
.cz-hex {
|
|
570
|
-
font-family: 'JetBrains Mono', monospace; font-size: 0.78rem; color: var(--accent2);
|
|
571
|
-
}
|
|
572
|
-
input[type='range'] {
|
|
573
|
-
width: 100%; accent-color: var(--accent); cursor: pointer;
|
|
574
|
-
}
|
|
575
|
-
.cz-toggles { display: flex; gap: 8px; flex-wrap: wrap; }
|
|
576
|
-
.cz-toggle { display: inline-flex; align-items: center; gap: 6px; font-size: 0.82rem; color: var(--muted); }
|
|
577
|
-
.cz-toggle input { accent-color: var(--accent); width: 15px; height: 15px; }
|
|
578
|
-
.cz-note { font-size: 0.72rem; color: var(--dim); text-align: center; }
|
|
579
|
-
/* ─────────── modals ─────────── */
|
|
580
|
-
|
|
581
|
-
.modal {
|
|
582
|
-
position: fixed; inset: 0; z-index: 150;
|
|
583
|
-
background: rgba(5, 3, 2, 0.82);
|
|
584
|
-
backdrop-filter: blur(14px);
|
|
585
|
-
display: grid; place-items: center;
|
|
586
|
-
animation: fadeIn 0.25s var(--ease);
|
|
587
|
-
}
|
|
588
|
-
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
589
|
-
|
|
590
|
-
.launcher-frame-wrap, .player-stage {
|
|
591
|
-
width: 100%; height: 100vh; overflow: hidden;
|
|
592
|
-
}
|
|
593
|
-
.launcher-bar, .player-bar {
|
|
594
|
-
position: absolute; top: 0; left: 0; right: 0; z-index: 5;
|
|
595
|
-
display: flex; align-items: center; gap: 10px;
|
|
596
|
-
padding: 10px 16px;
|
|
597
|
-
background: rgba(10, 7, 5, 0.75);
|
|
598
|
-
backdrop-filter: blur(14px);
|
|
599
|
-
border-bottom: 1px solid var(--line);
|
|
600
|
-
}
|
|
601
|
-
.launcher-title, .player-title {
|
|
602
|
-
font-weight: 700; font-size: 0.88rem; white-space: nowrap;
|
|
603
|
-
overflow: hidden; text-overflow: ellipsis; max-width: 40vw;
|
|
604
|
-
color: var(--accent2);
|
|
605
|
-
}
|
|
606
|
-
.launcher-actions { margin-left: auto; display: flex; gap: 7px; align-items: center; }
|
|
607
|
-
.launcher-actions .launcher-title { max-width: 30vw; }
|
|
608
|
-
#launcher-modal .modal-inner { width: 100%; }
|
|
609
|
-
#launcher-modal { display: flex; }
|
|
610
|
-
#launcher-modal .launcher-frame-wrap {
|
|
611
|
-
margin-top: 0; height: calc(100vh);
|
|
612
|
-
display: flex;
|
|
613
|
-
}
|
|
614
|
-
#launcher-frame {
|
|
615
|
-
flex: 1; border: none; height: 100%; width: 100%;
|
|
616
|
-
background: #000;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
#player-modal { display: flex; }
|
|
620
|
-
.player-bar { position: absolute; }
|
|
621
|
-
.player-stage {
|
|
622
|
-
width: 100%; display: grid; place-items: center;
|
|
623
|
-
padding-top: 58px;
|
|
624
|
-
}
|
|
625
|
-
.player-stage video {
|
|
626
|
-
max-width: 84vw; max-height: calc(100vh - 80px); border-radius: var(--radius);
|
|
627
|
-
box-shadow: 0 30px 90px rgba(0, 0, 0, 0.6); outline: none;
|
|
628
|
-
}
|
|
629
|
-
.player-stage iframe {
|
|
630
|
-
width: 84vw; height: calc(84vw * 0.5625);
|
|
631
|
-
max-height: calc(100vh - 90px); border: none; border-radius: var(--radius);
|
|
632
|
-
background: #000;
|
|
633
|
-
}
|
|
634
|
-
@media (max-height: 720px) {
|
|
635
|
-
.player-stage iframe { height: calc(100vh - 90px); }
|
|
636
|
-
}
|
|
637
|
-
.player-error {
|
|
638
|
-
max-width: 46ch; text-align: center; color: var(--muted);
|
|
639
|
-
padding: 30px; border-radius: var(--radius);
|
|
640
|
-
background: var(--panel-2); border: 1px dashed var(--line-2);
|
|
641
|
-
}
|
|
642
|
-
.player-error a { color: var(--accent2); text-decoration: underline; }
|
|
643
|
-
|
|
644
|
-
.form-card {
|
|
645
|
-
width: min(440px, 92vw); background: var(--panel-solid);
|
|
646
|
-
border: 1px solid var(--line); border-radius: calc(var(--radius) + 6px);
|
|
647
|
-
padding: 26px; display: grid; gap: 15px;
|
|
648
|
-
max-height: 90vh; overflow-y: auto;
|
|
649
|
-
}
|
|
650
|
-
.form-card h4 { font-size: 1.2rem; }
|
|
651
|
-
.form-field { display: grid; gap: 6px; }
|
|
652
|
-
.form-field label { font-size: 0.78rem; color: var(--muted); font-weight: 600; }
|
|
653
|
-
.form-field input {
|
|
654
|
-
background: rgba(0, 0, 0, 0.32); border: 1px solid var(--line);
|
|
655
|
-
border-radius: 10px; padding: 11px 14px; font-size: 0.92rem; outline: none;
|
|
656
|
-
transition: 0.22s var(--ease);
|
|
657
|
-
}
|
|
658
|
-
.form-field input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent); }
|
|
659
|
-
.form-row { display: flex; justify-content: flex-end; gap: 10px; }
|
|
660
|
-
|
|
661
|
-
/* ─────────── toasts ─────────── */
|
|
662
|
-
|
|
663
|
-
#toast-zone {
|
|
664
|
-
position: fixed; right: 18px; bottom: 18px; z-index: 300;
|
|
665
|
-
display: flex; flex-direction: column; gap: 10px; pointer-events: none;
|
|
666
|
-
}
|
|
667
|
-
.toast {
|
|
668
|
-
background: rgba(18, 12, 8, 0.92);
|
|
669
|
-
border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
|
|
670
|
-
border-left: 3px solid var(--accent);
|
|
671
|
-
color: var(--text); padding: 12px 18px; border-radius: 12px;
|
|
672
|
-
font-size: 0.85rem; max-width: 340px;
|
|
673
|
-
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5), 0 0 20px color-mix(in srgb, var(--accent) 15%, transparent);
|
|
674
|
-
backdrop-filter: blur(12px);
|
|
675
|
-
animation: toastIn 0.35s var(--ease);
|
|
676
|
-
}
|
|
677
|
-
.toast.out { animation: toastOut 0.35s var(--ease) forwards; }
|
|
678
|
-
@keyframes toastIn { from { transform: translateX(120%); opacity: 0; } to { transform: none; opacity: 1; } }
|
|
679
|
-
@keyframes toastOut { to { transform: translateX(120%); opacity: 0; } }
|
|
680
|
-
|
|
681
|
-
/* ─────────── turbo / confetti ─────────── */
|
|
682
|
-
|
|
683
|
-
.turbo { animation: turboBg 0.5s ease infinite alternate; }
|
|
684
|
-
@keyframes turboBg {
|
|
685
|
-
to { filter: hue-rotate(32deg) saturate(1.6); }
|
|
686
|
-
}
|
|
687
|
-
.confetti-piece {
|
|
688
|
-
position: fixed; width: 9px; height: 14px; z-index: 400;
|
|
689
|
-
border-radius: 2px; pointer-events: none;
|
|
690
|
-
animation: confettiFall linear forwards;
|
|
691
|
-
}
|
|
692
|
-
@keyframes confettiFall {
|
|
693
|
-
0% { transform: translateY(-10vh) rotate(0deg); opacity: 1; }
|
|
694
|
-
100% { transform: translateY(110vh) rotate(720deg); opacity: 0.4; }
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
/* ─────────── work mode ─────────── */
|
|
698
|
-
|
|
699
|
-
body[data-work='on'] .hero,
|
|
700
|
-
body[data-work='on'] #tunnel,
|
|
701
|
-
body[data-work='on'] #games,
|
|
702
|
-
body[data-work='on'] #movies,
|
|
703
|
-
body[data-work='on'] #continue-panel,
|
|
704
|
-
body[data-work='on'] .foot-hint,
|
|
705
|
-
body[data-work='on'] a[href='#tunnel'],
|
|
706
|
-
body[data-work='on'] a[href='#games'],
|
|
707
|
-
body[data-work='on'] a[href='#movies'] { display: none; }
|
|
708
|
-
body[data-work='on'] #work-view { display: block; }
|
|
709
|
-
|
|
710
|
-
#work-view { scroll-margin-top: 90px; }
|
|
711
|
-
.work-grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
|
|
712
|
-
.work-card {
|
|
713
|
-
background: var(--panel-2); border: 1px solid var(--line);
|
|
714
|
-
border-radius: var(--radius); padding: 18px;
|
|
715
|
-
}
|
|
716
|
-
.work-card h4 { font-size: 0.95rem; margin: 0 0 12px; color: var(--muted); }
|
|
717
|
-
.focus-timer {
|
|
718
|
-
font-family: 'JetBrains Mono', monospace; font-size: 1.9rem; font-weight: 700;
|
|
719
|
-
color: var(--accent2); letter-spacing: 1px;
|
|
720
|
-
}
|
|
721
|
-
.timer-controls { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
|
|
722
|
-
.todo-list { list-style: none; padding: 0; margin-top: 14px; max-height: 220px; overflow-y: auto; }
|
|
723
|
-
.todo-list li {
|
|
724
|
-
display: flex; align-items: center; gap: 10px; padding: 8px 12px;
|
|
725
|
-
border-radius: 10px; background: rgba(255, 255, 255, 0.045); margin-bottom: 6px;
|
|
726
|
-
}
|
|
727
|
-
.todo-list li.done span { text-decoration: line-through; opacity: 0.55; }
|
|
728
|
-
.todo-list input[type='checkbox'] { accent-color: var(--accent); width: 16px; height: 16px; cursor: pointer; }
|
|
729
|
-
.todo-list .icon-btn { width: 26px; height: 26px; font-size: 0.66rem; }
|
|
730
|
-
|
|
731
|
-
/* ─────────── responsive ─────────── */
|
|
732
|
-
|
|
733
|
-
@media (max-width: 720px) {
|
|
734
|
-
.nav-links { display: none; }
|
|
735
|
-
.hero { padding-top: 46px; }
|
|
736
|
-
.game-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }
|
|
737
|
-
.movie-grid { grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); }
|
|
738
|
-
.panel { padding: 18px; }
|
|
739
|
-
.player-stage video { max-width: 96vw; }
|
|
740
|
-
.player-stage iframe { width: 96vw; }
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
@media (prefers-reduced-motion: reduce) {
|
|
744
|
-
*, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
|
|
745
|
-
}
|
|
746
|
-
body[data-anim='off'] *, body[data-anim='off'] *::before, body[data-anim='off'] *::after {
|
|
747
|
-
animation: none !important;
|
|
748
|
-
scroll-behavior: auto;
|
|
749
|
-
}
|
|
8
|
+
:root{
|
|
9
|
+
--accent:#ff6b1a;
|
|
10
|
+
--bg:#050505;
|
|
11
|
+
--glass:rgba(255,255,255,.06);
|
|
12
|
+
--glass-border:rgba(255,255,255,.12);
|
|
13
|
+
--text:#f5f5f5;
|
|
14
|
+
}
|
|
15
|
+
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
|
|
16
|
+
html,body{height:100%;overflow:hidden;background:var(--bg);color:var(--text);
|
|
17
|
+
font-family:-apple-system,BlinkMacSystemFont,"SF Pro Display","Segoe UI",Inter,Roboto,sans-serif;}
|
|
18
|
+
::selection{background:var(--accent);color:#000}
|
|
19
|
+
button{font-family:inherit;cursor:pointer;border:none;background:none;color:inherit}
|
|
20
|
+
|
|
21
|
+
/* ---------- particles ---------- */
|
|
22
|
+
#particles{position:fixed;inset:0;z-index:0;pointer-events:none}
|
|
23
|
+
|
|
24
|
+
/* ---------- ambient glow (tiny bit of orange) ---------- */
|
|
25
|
+
.glow{position:fixed;z-index:0;pointer-events:none;filter:blur(120px);opacity:.14;border-radius:50%;
|
|
26
|
+
background:var(--accent);animation:glowFloat 14s ease-in-out infinite alternate}
|
|
27
|
+
.glow.g1{width:55vw;height:55vw;top:-20vw;right:-15vw}
|
|
28
|
+
.glow.g2{width:40vw;height:40vw;bottom:-15vw;left:-10vw;animation-delay:-7s}
|
|
29
|
+
@keyframes glowFloat{from{transform:translate(0,0) scale(1)}to{transform:translate(4vw,-3vw) scale(1.15)}}
|
|
30
|
+
|
|
31
|
+
/* ---------- screens ---------- */
|
|
32
|
+
.screen{position:fixed;inset:0;z-index:2;display:none;flex-direction:column;
|
|
33
|
+
opacity:0;transform:scale(.985);transition:opacity .45s cubic-bezier(.22,1,.36,1),transform .45s cubic-bezier(.22,1,.36,1)}
|
|
34
|
+
.screen.active{display:flex;opacity:1;transform:scale(1)}
|
|
35
|
+
|
|
36
|
+
/* ---------- liquid glass ---------- */
|
|
37
|
+
.glass{background:var(--glass);backdrop-filter:blur(28px) saturate(180%);
|
|
38
|
+
-webkit-backdrop-filter:blur(28px) saturate(180%);
|
|
39
|
+
border:1px solid var(--glass-border);border-radius:24px;position:relative;overflow:hidden;
|
|
40
|
+
box-shadow:0 8px 32px rgba(0,0,0,.45), inset 0 1px 0 rgba(255,255,255,.15)}
|
|
41
|
+
.glass::before{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
|
|
42
|
+
background:linear-gradient(135deg,rgba(255,255,255,.14),transparent 40%,transparent 70%,rgba(255,255,255,.05))}
|
|
43
|
+
|
|
44
|
+
/* ---------- HOME ---------- */
|
|
45
|
+
#home{padding:6vh 6vw 150px;overflow-y:auto;scrollbar-width:none;align-items:center;text-align:center;display:flex;flex-direction:column}
|
|
46
|
+
#home::-webkit-scrollbar{display:none}
|
|
47
|
+
.home-left{max-width:900px;width:100%;display:flex;flex-direction:column;align-items:center;animation:riseIn 1s cubic-bezier(.22,1,.36,1) both}
|
|
48
|
+
@keyframes riseIn{from{opacity:0;transform:translateY(40px)}to{opacity:1;transform:none}}
|
|
49
|
+
.os-badge{display:inline-flex;align-items:center;gap:8px;padding:6px 16px;border-radius:99px;
|
|
50
|
+
font-size:12px;letter-spacing:.25em;text-transform:uppercase;color:var(--accent);
|
|
51
|
+
border:1px solid color-mix(in srgb,var(--accent) 40%,transparent);margin-bottom:24px;
|
|
52
|
+
animation:pulseBadge 3s ease-in-out infinite}
|
|
53
|
+
@keyframes pulseBadge{0%,100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--accent) 35%,transparent)}
|
|
54
|
+
50%{box-shadow:0 0 24px 4px color-mix(in srgb,var(--accent) 25%,transparent)}}
|
|
55
|
+
.os-badge .dot{width:7px;height:7px;border-radius:50%;background:var(--accent);animation:blink 1.6s infinite}
|
|
56
|
+
@keyframes blink{50%{opacity:.25}}
|
|
57
|
+
#bigTitle{font-size:clamp(56px,9vw,124px);font-weight:800;line-height:.95;letter-spacing:-.03em;
|
|
58
|
+
background:linear-gradient(180deg,#fff 30%,#8a8a8a);-webkit-background-clip:text;background-clip:text;
|
|
59
|
+
-webkit-text-fill-color:transparent;animation:titleShimmer 6s ease-in-out infinite}
|
|
60
|
+
#bigTitle span{background:linear-gradient(135deg,var(--accent),color-mix(in srgb,var(--accent) 55%,#fff));
|
|
61
|
+
-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}
|
|
62
|
+
@keyframes titleShimmer{0%,100%{filter:drop-shadow(0 0 0 transparent)}50%{filter:drop-shadow(0 0 34px color-mix(in srgb,var(--accent) 30%,transparent))}}
|
|
63
|
+
#rotator{height:28px;margin-top:22px;font-size:clamp(15px,1.6vw,20px);color:#9a9a9a;overflow:hidden;position:relative;width:100%}
|
|
64
|
+
#rotator .line{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;gap:10px;
|
|
65
|
+
opacity:0;transform:translateY(24px);transition:opacity .6s,transform .6s cubic-bezier(.22,1,.36,1)}
|
|
66
|
+
#rotator .line.on{opacity:1;transform:none}
|
|
67
|
+
#rotator .line.off{opacity:0;transform:translateY(-24px)}
|
|
68
|
+
#rotator .tick{width:22px;height:2px;background:var(--accent);border-radius:2px;flex:none}
|
|
69
|
+
|
|
70
|
+
/* home widget row */
|
|
71
|
+
.widget-row{display:flex;gap:16px;margin-top:44px;flex-wrap:wrap;justify-content:center;animation:riseIn 1.1s .15s cubic-bezier(.22,1,.36,1) both}
|
|
72
|
+
.widget{padding:18px 22px;display:flex;align-items:center;gap:16px;min-width:200px;
|
|
73
|
+
transition:transform .35s cubic-bezier(.22,1,.36,1),box-shadow .35s}
|
|
74
|
+
.widget:hover{transform:translateY(-5px);box-shadow:0 16px 44px rgba(0,0,0,.6),0 0 30px color-mix(in srgb,var(--accent) 12%,transparent)}
|
|
75
|
+
.widget .wicon{font-size:30px;filter:drop-shadow(0 0 12px color-mix(in srgb,var(--accent) 45%,transparent))}
|
|
76
|
+
.widget .wlabel{font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:#888}
|
|
77
|
+
.widget .wvalue{font-size:20px;font-weight:700;margin-top:2px}
|
|
78
|
+
/* battery ring */
|
|
79
|
+
.ring{transform:rotate(-90deg)}
|
|
80
|
+
.ring .bg{stroke:rgba(255,255,255,.1)}
|
|
81
|
+
.ring .fg{stroke:var(--accent);stroke-linecap:round;transition:stroke-dashoffset 1s cubic-bezier(.22,1,.36,1)}
|
|
82
|
+
|
|
83
|
+
/* home nav */
|
|
84
|
+
.home-nav{display:flex;gap:14px;margin-top:46px;flex-wrap:wrap;justify-content:center;animation:riseIn 1.2s .3s cubic-bezier(.22,1,.36,1) both}
|
|
85
|
+
.nav-btn{padding:14px 30px;border-radius:18px;font-size:15px;font-weight:600;letter-spacing:.04em;
|
|
86
|
+
display:flex;align-items:center;gap:10px;transition:all .3s cubic-bezier(.22,1,.36,1)}
|
|
87
|
+
.nav-btn:hover{transform:translateY(-4px) scale(1.03);border-color:color-mix(in srgb,var(--accent) 55%,transparent);
|
|
88
|
+
box-shadow:0 12px 34px rgba(0,0,0,.55),0 0 26px color-mix(in srgb,var(--accent) 18%,transparent)}
|
|
89
|
+
.nav-btn .arr{transition:transform .3s}
|
|
90
|
+
.nav-btn:hover .arr{transform:translateX(5px)}
|
|
91
|
+
.nav-btn.primary{background:linear-gradient(135deg,var(--accent),color-mix(in srgb,var(--accent) 70%,#000));
|
|
92
|
+
color:#0a0a0a;box-shadow:0 8px 26px color-mix(in srgb,var(--accent) 35%,transparent)}
|
|
93
|
+
|
|
94
|
+
/* ---------- home library marquee ---------- */
|
|
95
|
+
.library{width:100%;max-width:1200px;margin-top:80px;display:flex;flex-direction:column;gap:20px;
|
|
96
|
+
animation:riseIn 1.3s .4s cubic-bezier(.22,1,.36,1) both}
|
|
97
|
+
.library .lib-head{font-size:11px;letter-spacing:.4em;text-transform:uppercase;color:#666;
|
|
98
|
+
display:flex;align-items:center;justify-content:center;gap:16px}
|
|
99
|
+
.library .lib-head::before,.library .lib-head::after{content:"";width:70px;height:1px;
|
|
100
|
+
background:linear-gradient(90deg,transparent,rgba(255,255,255,.25))}
|
|
101
|
+
.library .lib-head::after{background:linear-gradient(90deg,rgba(255,255,255,.25),transparent)}
|
|
102
|
+
.rail-window{overflow:hidden;width:100%;position:relative;padding:6px 0;
|
|
103
|
+
-webkit-mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
|
|
104
|
+
mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent)}
|
|
105
|
+
.rail-track{display:flex;gap:18px;width:max-content;padding-right:18px;
|
|
106
|
+
animation:railScroll 42s linear infinite}
|
|
107
|
+
.rail-track.rev{animation-direction:reverse;animation-duration:50s}
|
|
108
|
+
.rail-window:hover .rail-track{animation-play-state:paused}
|
|
109
|
+
@keyframes railScroll{from{transform:translateX(0)}to{transform:translateX(-50%)}}
|
|
110
|
+
.rail-game{width:92px;height:92px;flex:none;border-radius:24px;overflow:hidden;
|
|
111
|
+
position:relative;border:1px solid var(--glass-border);cursor:pointer;
|
|
112
|
+
transition:transform .35s cubic-bezier(.34,1.56,.64,1),box-shadow .35s}
|
|
113
|
+
.rail-game:hover{transform:scale(1.15) rotate(-3deg);box-shadow:0 10px 30px rgba(0,0,0,.6),0 0 24px var(--gc,#ff6b1a)}
|
|
114
|
+
.rail-game img{width:100%;height:100%;object-fit:cover;display:block}
|
|
115
|
+
|
|
116
|
+
/* ---------- section headers (games / movies) ---------- */
|
|
117
|
+
.hub{padding:34px 5vw 130px;overflow-y:auto;scrollbar-width:none}
|
|
118
|
+
.hub::-webkit-scrollbar{display:none}
|
|
119
|
+
.hub-head{display:flex;align-items:flex-end;justify-content:space-between;margin-bottom:34px;
|
|
120
|
+
animation:riseIn .7s cubic-bezier(.22,1,.36,1) both}
|
|
121
|
+
.hub-head h2{font-size:clamp(34px,4.5vw,58px);font-weight:800;letter-spacing:-.02em}
|
|
122
|
+
.hub-head h2 em{font-style:normal;color:var(--accent)}
|
|
123
|
+
.hub-head .count{font-size:13px;color:#777;letter-spacing:.15em;text-transform:uppercase}
|
|
124
|
+
|
|
125
|
+
/* games grid: rows of 5, icons only */
|
|
126
|
+
.games-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:22px}
|
|
127
|
+
@media(max-width:1100px){.games-grid{grid-template-columns:repeat(4,1fr)}}
|
|
128
|
+
@media(max-width:800px){.games-grid{grid-template-columns:repeat(3,1fr)}}
|
|
129
|
+
@media(max-width:520px){.games-grid{grid-template-columns:repeat(2,1fr)}}
|
|
130
|
+
.game-tile{aspect-ratio:1;border-radius:26px;overflow:hidden;position:relative;cursor:pointer;
|
|
131
|
+
border:1px solid var(--glass-border);
|
|
132
|
+
animation:tileIn .7s cubic-bezier(.22,1,.36,1) both;animation-delay:var(--d,0s);
|
|
133
|
+
transition:transform .4s cubic-bezier(.34,1.56,.64,1),box-shadow .4s}
|
|
134
|
+
@keyframes tileIn{from{opacity:0;transform:translateY(34px) scale(.92)}to{opacity:1;transform:none}}
|
|
135
|
+
.game-tile:hover{transform:translateY(-8px) scale(1.04);z-index:2;
|
|
136
|
+
box-shadow:0 20px 50px rgba(0,0,0,.65),0 0 34px var(--gc,#ff6b1a)}
|
|
137
|
+
.game-tile img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .6s cubic-bezier(.22,1,.36,1)}
|
|
138
|
+
.game-tile:hover img{transform:scale(1.12)}
|
|
139
|
+
.game-tile .shine{position:absolute;inset:0;pointer-events:none;
|
|
140
|
+
background:linear-gradient(115deg,transparent 30%,rgba(255,255,255,.22) 48%,transparent 62%);
|
|
141
|
+
transform:translateX(-120%);transition:transform .7s ease}
|
|
142
|
+
.game-tile:hover .shine{transform:translateX(120%)}
|
|
143
|
+
.game-tile .tip{position:absolute;left:50%;bottom:-8px;transform:translate(-50%,10px);opacity:0;
|
|
144
|
+
background:rgba(10,10,10,.88);backdrop-filter:blur(10px);border:1px solid var(--glass-border);
|
|
145
|
+
padding:7px 15px;border-radius:12px;font-size:13px;font-weight:600;white-space:nowrap;pointer-events:none;
|
|
146
|
+
transition:all .3s cubic-bezier(.22,1,.36,1);z-index:3}
|
|
147
|
+
.game-tile:hover .tip{opacity:1;transform:translate(-50%,0)}
|
|
148
|
+
.game-tile .play-hint{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
|
|
149
|
+
background:rgba(0,0,0,.45);opacity:0;transition:opacity .3s}
|
|
150
|
+
.game-tile:hover .play-hint{opacity:1}
|
|
151
|
+
.play-hint .tri{width:0;height:0;border-left:22px solid #fff;border-top:13px solid transparent;border-bottom:13px solid transparent;
|
|
152
|
+
filter:drop-shadow(0 0 14px var(--accent));margin-left:6px}
|
|
153
|
+
.game-tile .feat-tag{position:absolute;top:10px;left:10px;background:var(--accent);color:#000;
|
|
154
|
+
font-size:9px;font-weight:800;letter-spacing:.12em;padding:4px 9px;border-radius:8px}
|
|
155
|
+
|
|
156
|
+
/* movies grid: no names */
|
|
157
|
+
.movies-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:26px}
|
|
158
|
+
.movie-tile{border-radius:20px;overflow:hidden;cursor:pointer;position:relative;aspect-ratio:2/3;
|
|
159
|
+
border:1px solid var(--glass-border);
|
|
160
|
+
animation:tileIn .7s cubic-bezier(.22,1,.36,1) both;animation-delay:var(--d,0s);
|
|
161
|
+
transition:transform .45s cubic-bezier(.22,1,.36,1),box-shadow .45s}
|
|
162
|
+
.movie-tile:hover{transform:translateY(-10px) scale(1.05);box-shadow:0 24px 60px rgba(0,0,0,.7),0 0 40px color-mix(in srgb,var(--accent) 20%,transparent)}
|
|
163
|
+
.movie-tile img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .6s cubic-bezier(.22,1,.36,1)}
|
|
164
|
+
.movie-tile:hover img{transform:scale(1.1)}
|
|
165
|
+
.movie-tile .veil{position:absolute;inset:0;background:linear-gradient(180deg,transparent 55%,rgba(0,0,0,.75));
|
|
166
|
+
opacity:0;transition:opacity .35s;display:flex;align-items:flex-end;justify-content:center;padding-bottom:20px}
|
|
167
|
+
.movie-tile:hover .veil{opacity:1}
|
|
168
|
+
.movie-tile .tri{width:0;height:0;border-left:20px solid #fff;border-top:12px solid transparent;border-bottom:12px solid transparent;
|
|
169
|
+
filter:drop-shadow(0 0 14px var(--accent));transition:transform .3s}
|
|
170
|
+
.movie-tile:hover .tri{transform:scale(1.2)}
|
|
171
|
+
|
|
172
|
+
/* ---------- play modal ---------- */
|
|
173
|
+
#playModal{position:fixed;inset:0;z-index:50;display:none;align-items:center;justify-content:center;
|
|
174
|
+
background:rgba(0,0,0,.82);backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px)}
|
|
175
|
+
#playModal.open{display:flex;animation:fadeIn .3s ease}
|
|
176
|
+
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
|
|
177
|
+
.play-shell{width:min(1100px,94vw);height:min(86vh,90vh);border-radius:26px;overflow:hidden;
|
|
178
|
+
display:flex;flex-direction:column;border:1px solid var(--glass-border);
|
|
179
|
+
background:#0a0a0a;box-shadow:0 40px 120px rgba(0,0,0,.8),0 0 60px color-mix(in srgb,var(--accent) 10%,transparent);
|
|
180
|
+
animation:popIn .45s cubic-bezier(.34,1.56,.64,1)}
|
|
181
|
+
@keyframes popIn{from{opacity:0;transform:scale(.9)}to{opacity:1;transform:none}}
|
|
182
|
+
.play-bar{display:flex;align-items:center;gap:14px;padding:12px 18px;
|
|
183
|
+
background:rgba(255,255,255,.04);border-bottom:1px solid var(--glass-border)}
|
|
184
|
+
.play-bar .ttl{font-weight:700;font-size:15px;letter-spacing:.02em;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
185
|
+
.play-bar .live{width:8px;height:8px;border-radius:50%;background:var(--accent);animation:blink 1.4s infinite;flex:none}
|
|
186
|
+
.play-bar button{padding:8px 18px;border-radius:12px;font-size:13px;font-weight:700;
|
|
187
|
+
background:rgba(255,255,255,.08);border:1px solid var(--glass-border);transition:all .25s}
|
|
188
|
+
.play-bar button:hover{background:var(--accent);color:#000}
|
|
189
|
+
#playFrame{flex:1;width:100%;border:none;background:#000}
|
|
190
|
+
.play-loading{position:absolute;inset:44px 0 0;display:flex;flex-direction:column;gap:18px;
|
|
191
|
+
align-items:center;justify-content:center;background:#0a0a0a;transition:opacity .4s;z-index:2}
|
|
192
|
+
.play-loading.gone{opacity:0;pointer-events:none}
|
|
193
|
+
.spinner{width:52px;height:52px;border-radius:50%;border:3px solid rgba(255,255,255,.1);
|
|
194
|
+
border-top-color:var(--accent);animation:spin 1s linear infinite}
|
|
195
|
+
@keyframes spin{to{transform:rotate(360deg)}}
|
|
196
|
+
|
|
197
|
+
/* ---------- bottom nav ---------- */
|
|
198
|
+
#dock{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);z-index:20;display:flex;gap:6px;
|
|
199
|
+
padding:8px;border-radius:24px;animation:riseIn 1s .5s cubic-bezier(.22,1,.36,1) both}
|
|
200
|
+
.dock-btn{padding:12px 22px;border-radius:17px;font-size:14px;font-weight:600;color:#aaa;
|
|
201
|
+
display:flex;align-items:center;gap:8px;transition:all .3s cubic-bezier(.22,1,.36,1);position:relative}
|
|
202
|
+
.dock-btn:hover{color:#fff;transform:translateY(-3px)}
|
|
203
|
+
.dock-btn.on{color:#0a0a0a;background:linear-gradient(135deg,var(--accent),color-mix(in srgb,var(--accent) 75%,#000));
|
|
204
|
+
box-shadow:0 6px 22px color-mix(in srgb,var(--accent) 40%,transparent)}
|
|
205
|
+
.dock-btn .dicon{font-size:16px}
|
|
206
|
+
|
|
207
|
+
/* ---------- settings ---------- */
|
|
208
|
+
#settingsModal{position:fixed;inset:0;z-index:60;display:none;align-items:center;justify-content:center;
|
|
209
|
+
background:rgba(0,0,0,.7);backdrop-filter:blur(14px)}
|
|
210
|
+
#settingsModal.open{display:flex;animation:fadeIn .25s ease}
|
|
211
|
+
.settings-card{width:min(560px,92vw);max-height:86vh;overflow-y:auto;padding:32px;scrollbar-width:none;
|
|
212
|
+
animation:popIn .4s cubic-bezier(.34,1.56,.64,1)}
|
|
213
|
+
.settings-card::-webkit-scrollbar{display:none}
|
|
214
|
+
.settings-card h3{font-size:26px;font-weight:800;margin-bottom:6px}
|
|
215
|
+
.settings-card .sub{color:#888;font-size:13px;margin-bottom:26px}
|
|
216
|
+
.set-row{display:flex;align-items:center;justify-content:space-between;gap:18px;padding:16px 0;
|
|
217
|
+
border-bottom:1px solid rgba(255,255,255,.07)}
|
|
218
|
+
.set-row .lbl{font-size:14px;font-weight:600}
|
|
219
|
+
.set-row .hint{font-size:11px;color:#777;margin-top:3px}
|
|
220
|
+
.swatches{display:flex;gap:10px;flex-wrap:wrap}
|
|
221
|
+
.swatch{width:36px;height:36px;border-radius:12px;cursor:pointer;border:2px solid transparent;transition:all .25s}
|
|
222
|
+
.swatch:hover{transform:scale(1.15)}
|
|
223
|
+
.swatch.on{border-color:#fff;transform:scale(1.15);box-shadow:0 0 16px currentColor}
|
|
224
|
+
input[type=text],input[type=range]{accent-color:var(--accent)}
|
|
225
|
+
.set-input{background:rgba(255,255,255,.07);border:1px solid var(--glass-border);color:#fff;
|
|
226
|
+
padding:10px 14px;border-radius:12px;font-size:14px;width:170px;outline:none;transition:border .25s}
|
|
227
|
+
.set-input:focus{border-color:var(--accent)}
|
|
228
|
+
|
|
229
|
+
/* ---------- AI ---------- */
|
|
230
|
+
#ai{padding:0}
|
|
231
|
+
.ai-wrap{flex:1;display:flex;flex-direction:column;max-width:900px;width:100%;margin:0 auto;padding:30px 4vw 120px}
|
|
232
|
+
.ai-head{display:flex;align-items:center;gap:16px;margin-bottom:20px;animation:riseIn .6s both}
|
|
233
|
+
.ai-orb{width:52px;height:52px;border-radius:50%;flex:none;position:relative;
|
|
234
|
+
background:radial-gradient(circle at 32% 30%,color-mix(in srgb,var(--accent) 80%,#fff),var(--accent) 45%,#3a1500);
|
|
235
|
+
animation:orbFloat 4s ease-in-out infinite;box-shadow:0 0 34px color-mix(in srgb,var(--accent) 45%,transparent)}
|
|
236
|
+
@keyframes orbFloat{0%,100%{transform:translateY(0)}50%{transform:translateY(-7px)}}
|
|
237
|
+
.ai-orb::after{content:"";position:absolute;inset:-7px;border-radius:50%;
|
|
238
|
+
border:1px solid color-mix(in srgb,var(--accent) 45%,transparent);animation:orbSpin 7s linear infinite}
|
|
239
|
+
@keyframes orbSpin{to{transform:rotate(360deg)}}
|
|
240
|
+
.ai-head h2{font-size:26px;font-weight:800}
|
|
241
|
+
.ai-head p{font-size:13px;color:#999}
|
|
242
|
+
#chatLog{flex:1;overflow-y:auto;display:flex;flex-direction:column;gap:16px;padding:10px 4px;scrollbar-width:none}
|
|
243
|
+
#chatLog::-webkit-scrollbar{display:none}
|
|
244
|
+
.msg{max-width:82%;padding:15px 19px;border-radius:22px;font-size:15px;line-height:1.55;
|
|
245
|
+
animation:msgIn .45s cubic-bezier(.22,1,.36,1) both;white-space:pre-wrap;word-break:break-word}
|
|
246
|
+
@keyframes msgIn{from{opacity:0;transform:translateY(16px) scale(.97)}to{opacity:1;transform:none}}
|
|
247
|
+
.msg.user{align-self:flex-end;background:linear-gradient(135deg,var(--accent),color-mix(in srgb,var(--accent) 75%,#000));
|
|
248
|
+
color:#0d0d0d;font-weight:500;border-bottom-right-radius:6px}
|
|
249
|
+
.msg.bot{align-self:flex-start;background:var(--glass);backdrop-filter:blur(20px);
|
|
250
|
+
border:1px solid var(--glass-border);border-bottom-left-radius:6px}
|
|
251
|
+
.msg.bot .who{font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--accent);font-weight:800;margin-bottom:6px}
|
|
252
|
+
/* code widget */
|
|
253
|
+
.code-widget{margin:12px 0;border-radius:16px;overflow:hidden;border:1px solid var(--glass-border);
|
|
254
|
+
background:#0b0b0f;box-shadow:0 8px 24px rgba(0,0,0,.4)}
|
|
255
|
+
.code-head{display:flex;align-items:center;gap:10px;padding:9px 14px;background:rgba(255,255,255,.05);
|
|
256
|
+
border-bottom:1px solid var(--glass-border)}
|
|
257
|
+
.code-head .dots{display:flex;gap:6px}
|
|
258
|
+
.code-head .dots i{width:10px;height:10px;border-radius:50%;background:rgba(255,255,255,.15)}
|
|
259
|
+
.code-head .dots i:first-child{background:var(--accent)}
|
|
260
|
+
.code-head .lang{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:#888;flex:1}
|
|
261
|
+
.code-head .collapse,.code-head .copy{font-size:11px;font-weight:700;padding:5px 12px;border-radius:9px;
|
|
262
|
+
background:rgba(255,255,255,.07);border:1px solid var(--glass-border);transition:all .2s}
|
|
263
|
+
.code-head .copy:hover{background:var(--accent);color:#000}
|
|
264
|
+
.code-widget pre{max-height:220px;overflow:auto;padding:15px;font-size:12.5px;line-height:1.6;
|
|
265
|
+
font-family:"SF Mono",Menlo,Consolas,monospace;color:#d8d8e8;scrollbar-width:thin;transition:max-height .4s ease}
|
|
266
|
+
.code-widget.collapsed pre{max-height:0;padding-top:0;padding-bottom:0}
|
|
267
|
+
.code-widget pre::-webkit-scrollbar{width:6px;height:6px}
|
|
268
|
+
.code-widget pre::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--accent) 50%,transparent);border-radius:3px}
|
|
269
|
+
.typing{align-self:flex-start;display:flex;gap:6px;padding:16px 20px}
|
|
270
|
+
.typing i{width:8px;height:8px;border-radius:50%;background:var(--accent);animation:tp 1.2s infinite}
|
|
271
|
+
.typing i:nth-child(2){animation-delay:.18s}.typing i:nth-child(3){animation-delay:.36s}
|
|
272
|
+
@keyframes tp{0%,60%,100%{transform:translateY(0);opacity:.4}30%{transform:translateY(-7px);opacity:1}}
|
|
273
|
+
#aiInputBar{display:flex;gap:12px;margin-top:18px;padding:10px 10px 10px 22px;align-items:center;
|
|
274
|
+
animation:riseIn .8s .2s both}
|
|
275
|
+
#aiInput{flex:1;background:none;border:none;outline:none;color:#fff;font-size:15px;font-family:inherit}
|
|
276
|
+
#aiInput::placeholder{color:#666}
|
|
277
|
+
#sendBtn{width:48px;height:48px;border-radius:16px;flex:none;display:flex;align-items:center;justify-content:center;
|
|
278
|
+
background:linear-gradient(135deg,var(--accent),color-mix(in srgb,var(--accent) 70%,#000));color:#0a0a0a;
|
|
279
|
+
font-size:19px;transition:transform .25s,box-shadow .25s;box-shadow:0 6px 20px color-mix(in srgb,var(--accent) 35%,transparent)}
|
|
280
|
+
#sendBtn:hover{transform:scale(1.1) rotate(-8deg)}
|
|
281
|
+
.chips{display:flex;gap:10px;margin-top:14px;flex-wrap:wrap}
|
|
282
|
+
.chip{font-size:12px;padding:8px 16px;border-radius:99px;border:1px solid var(--glass-border);
|
|
283
|
+
color:#aaa;background:rgba(255,255,255,.04);transition:all .25s}
|
|
284
|
+
.chip:hover{color:#fff;border-color:var(--accent);transform:translateY(-2px)}
|
|
285
|
+
|
|
286
|
+
/* toast */
|
|
287
|
+
#toast{position:fixed;left:50%;bottom:96px;transform:translate(-50%,20px);z-index:99;opacity:0;
|
|
288
|
+
background:rgba(15,15,15,.9);backdrop-filter:blur(14px);border:1px solid var(--glass-border);
|
|
289
|
+
padding:11px 22px;border-radius:14px;font-size:13px;font-weight:600;pointer-events:none;
|
|
290
|
+
transition:all .35s cubic-bezier(.22,1,.36,1)}
|
|
291
|
+
#toast.on{opacity:1;transform:translate(-50%,0)}
|
|
750
292
|
</style>
|
|
751
|
-
|
|
752
|
-
<base target="_blank">
|
|
753
293
|
<base target="_blank">
|
|
754
294
|
</head>
|
|
755
|
-
<body
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
<div class="
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
<div class="
|
|
764
|
-
|
|
765
|
-
</
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
<div
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
<div class="boot-type" id="boot-type"></div>
|
|
773
|
-
<div class="boot-bar"><i id="boot-fill"></i></div>
|
|
774
|
-
<button id="boot-skip" class="btn ghost btn-sm">skip boot →</button>
|
|
775
|
-
</div>
|
|
776
|
-
</div>
|
|
777
|
-
|
|
778
|
-
<!-- nav -->
|
|
779
|
-
<header class="nav" id="nav">
|
|
780
|
-
<div class="nav-inner">
|
|
781
|
-
<a class="brand" href="#top">
|
|
782
|
-
<span class="brand-mark">▲</span>
|
|
783
|
-
<span class="brand-name">Ignite<span class="accent-text">OS</span></span>
|
|
784
|
-
<span class="brand-ver">v1.5</span>
|
|
785
|
-
</a>
|
|
786
|
-
<nav class="nav-links" id="nav-links">
|
|
787
|
-
<a href="#games" class="nav-link">Games</a>
|
|
788
|
-
<a href="#movies" class="nav-link">Movies</a>
|
|
789
|
-
<button class="nav-link btn-link" id="nav-work" type="button" title="Work Mode hides the vault (hotkey H)">Work</button>
|
|
790
|
-
<button class="nav-link btn-link" id="nav-customize" type="button">Customize</button>
|
|
791
|
-
</nav>
|
|
792
|
-
<div class="nav-side">
|
|
793
|
-
<span class="nav-clock" id="nav-clock">--:--</span>
|
|
794
|
-
<span class="profile-pill" id="profile-pill" title="Your driver name — click to change">DRIVER</span>
|
|
795
|
-
</div>
|
|
796
|
-
</div>
|
|
797
|
-
</header>
|
|
798
|
-
|
|
799
|
-
<main id="top">
|
|
800
|
-
<!-- hero -->
|
|
801
|
-
<!-- work mode view -->
|
|
802
|
-
<section class="panel hidden" id="work-view">
|
|
803
|
-
<div class="panel-head">
|
|
804
|
-
<div><h3 class="panel-title">📖 Study Focus</h3>
|
|
805
|
-
<p class="panel-sub">Focus timer + task list. Work Mode hides the rest of the vault — press <kbd>H</kbd> to switch.</p></div>
|
|
806
|
-
<div class="focus-timer" id="focus-timer">25:00</div>
|
|
807
|
-
</div>
|
|
808
|
-
<div class="work-grid">
|
|
809
|
-
<div class="work-card">
|
|
810
|
-
<h4>Focus timer</h4>
|
|
811
|
-
<div class="timer-controls">
|
|
812
|
-
<button class="btn ghost btn-sm" id="timer-start" type="button">▶ start</button>
|
|
813
|
-
<button class="btn ghost btn-sm" id="timer-pause" type="button">⏸ pause</button>
|
|
814
|
-
<button class="btn ghost btn-sm" id="timer-reset" type="button">⟳ reset</button>
|
|
815
|
-
</div>
|
|
816
|
-
<select id="timer-length" class="select-mini">
|
|
817
|
-
<option value="1500">25:00 pomodoro</option>
|
|
818
|
-
<option value="600">10:00</option>
|
|
819
|
-
<option value="1800">30:00</option>
|
|
820
|
-
<option value="3600">60:00</option>
|
|
821
|
-
</select>
|
|
295
|
+
<body>
|
|
296
|
+
|
|
297
|
+
<canvas id="particles"></canvas>
|
|
298
|
+
<div class="glow g1"></div>
|
|
299
|
+
<div class="glow g2"></div>
|
|
300
|
+
|
|
301
|
+
<!-- ============ HOME ============ -->
|
|
302
|
+
<div class="screen active" id="home">
|
|
303
|
+
<div class="home-left">
|
|
304
|
+
<div class="os-badge"><span class="dot"></span>IGNITE OS · v2.0</div>
|
|
305
|
+
<h1 id="bigTitle">IGNITE<br><span>OS</span></h1>
|
|
306
|
+
<div id="rotator"></div>
|
|
307
|
+
|
|
308
|
+
<div class="widget-row">
|
|
309
|
+
<div class="widget glass">
|
|
310
|
+
<div class="wicon" id="wIcon">⛅</div>
|
|
311
|
+
<div><div class="wlabel">Weather</div><div class="wvalue" id="wVal">--°</div></div>
|
|
822
312
|
</div>
|
|
823
|
-
<div class="
|
|
824
|
-
<
|
|
825
|
-
|
|
826
|
-
<
|
|
827
|
-
|
|
828
|
-
</
|
|
829
|
-
<
|
|
830
|
-
|
|
313
|
+
<div class="widget glass">
|
|
314
|
+
<svg class="ring" width="52" height="52" viewBox="0 0 52 52">
|
|
315
|
+
<circle class="bg" cx="26" cy="26" r="21" fill="none" stroke-width="5"/>
|
|
316
|
+
<circle class="fg" id="battRing" cx="26" cy="26" r="21" fill="none" stroke-width="5"
|
|
317
|
+
stroke-dasharray="131.9" stroke-dashoffset="131.9"/>
|
|
318
|
+
</svg>
|
|
319
|
+
<div><div class="wlabel">Battery</div><div class="wvalue" id="battVal">--%</div></div>
|
|
320
|
+
</div>
|
|
321
|
+
<div class="widget glass">
|
|
322
|
+
<div class="wicon">🕒</div>
|
|
323
|
+
<div><div class="wlabel">Time</div><div class="wvalue" id="clockVal">--:--</div></div>
|
|
831
324
|
</div>
|
|
832
325
|
</div>
|
|
833
|
-
</section>
|
|
834
|
-
<section class="hero">
|
|
835
|
-
<div class="hero-eyebrow"><span class="pulse-dot"></span> systems online</div>
|
|
836
|
-
<h2 class="hero-title">Everything, <span class="accent-text gradient-text">unlocked.</span></h2>
|
|
837
|
-
<p class="hero-sub">IGNITE OS is a dashboard for browser games and movies streamed straight from your Google Drive.</p>
|
|
838
|
-
<div class="stat-strip" id="stat-strip">
|
|
839
|
-
<div class="stat"><b id="stat-games">0</b><span>games</span></div>
|
|
840
|
-
<div class="stat"><b id="stat-movies">0</b><span>movies</span></div>
|
|
841
|
-
<div class="stat"><b id="stat-local">0</b><span>local</span></div>
|
|
842
|
-
<div class="stat"><b id="stat-last">—</b><span>last launch</span></div>
|
|
843
|
-
</div>
|
|
844
|
-
</section>
|
|
845
|
-
|
|
846
|
-
<!-- games -->
|
|
847
|
-
<section class="panel" id="games">
|
|
848
|
-
<div class="panel-head">
|
|
849
|
-
<div><h3 class="panel-title">🎮 Game Vault</h3>
|
|
850
|
-
<p class="panel-sub">Play proxied web games or drop your own into <code>public/local-games/</code>.</p></div>
|
|
851
|
-
<span class="badge" id="games-edit-badge">+ new</span>
|
|
852
|
-
</div>
|
|
853
|
-
<div id="local-hint" class="local-hint hidden">
|
|
854
|
-
📁 no local games yet — drop any HTML5 game folder into <code>public/local-games/</code> and hit refresh. They run fully offline.
|
|
855
|
-
</div>
|
|
856
|
-
<div class="game-grid" id="game-grid"></div>
|
|
857
|
-
</section>
|
|
858
326
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
<
|
|
863
|
-
<p class="panel-sub">Paste any Google Drive share link (file <em>or</em> folder) — IGNITE OS streams it in a full player.</p></div>
|
|
864
|
-
<span class="badge" id="movies-add">+ movie</span>
|
|
327
|
+
<div class="home-nav">
|
|
328
|
+
<button class="nav-btn primary glass" onclick="go('games')">Enter Games Hub <span class="arr">→</span></button>
|
|
329
|
+
<button class="nav-btn glass" onclick="go('movies')">Movies <span class="arr">→</span></button>
|
|
330
|
+
<button class="nav-btn glass" onclick="go('ai')">Ask IGNITE AI <span class="arr">→</span></button>
|
|
865
331
|
</div>
|
|
866
|
-
|
|
867
|
-
<input id="movie-search" class="search-input" type="text" placeholder="search movies…">
|
|
868
|
-
<div class="genre-chips" id="genre-chips"></div>
|
|
869
|
-
<select id="movie-sort" class="select-mini">
|
|
870
|
-
<option value="added">newest first</option>
|
|
871
|
-
<option value="title">title A→Z</option>
|
|
872
|
-
<option value="watched">unwatched first</option>
|
|
873
|
-
</select>
|
|
874
|
-
</div>
|
|
875
|
-
<div class="movie-grid" id="movie-grid"></div>
|
|
876
|
-
<div class="empty-state hidden" id="movies-empty">
|
|
877
|
-
<div class="empty-art">🍻</div>
|
|
878
|
-
<h4>Your library is empty</h4>
|
|
879
|
-
<p>Hit <b>+ movie</b> and paste a Google Drive link, or load the demo shelf to see the player in action.</p>
|
|
880
|
-
<button class="btn ghost" id="demo-movies">load demo shelf</button>
|
|
881
|
-
</div>
|
|
882
|
-
</section>
|
|
332
|
+
</div>
|
|
883
333
|
|
|
884
|
-
<!--
|
|
885
|
-
<
|
|
886
|
-
<div class="
|
|
887
|
-
<div class="
|
|
888
|
-
|
|
334
|
+
<!-- library marquee -->
|
|
335
|
+
<div class="library">
|
|
336
|
+
<div class="lib-head">Game Library · hover to pause · click to play</div>
|
|
337
|
+
<div class="rail-window"><div class="rail-track" id="railTrack"></div></div>
|
|
338
|
+
<div class="rail-window"><div class="rail-track rev" id="railTrack2"></div></div>
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
889
341
|
|
|
890
|
-
</main>
|
|
891
342
|
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
<
|
|
895
|
-
|
|
896
|
-
</
|
|
897
|
-
|
|
898
|
-
<aside class="customizer" id="customizer" aria-label="Customizer">
|
|
899
|
-
<div class="customizer-head">
|
|
900
|
-
<h4>Customizer</h4>
|
|
901
|
-
<button class="icon-btn" id="cz-close" title="close">✕</button>
|
|
902
|
-
</div>
|
|
903
|
-
<div class="cz-scroll">
|
|
904
|
-
<label class="cz-label">theme preset</label>
|
|
905
|
-
<div class="swatches" id="cz-swatches">
|
|
906
|
-
<button class="swatch" data-preset="ember" style="--sw:#ff6b1a" title="Ember"></button>
|
|
907
|
-
<button class="swatch" data-preset="cobalt" style="--sw:#3b82f6" title="Cobalt"></button>
|
|
908
|
-
<button class="swatch" data-preset="violet" style="--sw:#a855f7" title="Violet"></button>
|
|
909
|
-
<button class="swatch" data-preset="inferno" style="--sw:#ff3d3d" title="Inferno"></button>
|
|
910
|
-
<button class="swatch" data-preset="ocean" style="--sw:#22d3ee" title="Ocean"></button>
|
|
911
|
-
<button class="swatch" data-preset="mint" style="--sw:#34d399" title="Mint"></button>
|
|
912
|
-
</div>
|
|
913
|
-
<label class="cz-label" for="cz-accent">accent color</label>
|
|
914
|
-
<div class="cz-row">
|
|
915
|
-
<input type="color" id="cz-accent" value="#ff6b1a">
|
|
916
|
-
<span class="cz-hex" id="cz-accent-hex">#ff6b1a</span>
|
|
917
|
-
</div>
|
|
918
|
-
<label class="cz-label" for="cz-accent2">secondary accent</label>
|
|
919
|
-
<div class="cz-row">
|
|
920
|
-
<input type="color" id="cz-accent2" value="#ffb26b">
|
|
921
|
-
<span class="cz-hex" id="cz-accent2-hex">#ffb26b</span>
|
|
343
|
+
<!-- ============ GAMES HUB ============ -->
|
|
344
|
+
<div class="screen" id="games">
|
|
345
|
+
<div class="hub">
|
|
346
|
+
<div class="hub-head">
|
|
347
|
+
<h2>Games <em>Hub</em></h2>
|
|
348
|
+
<div class="count" id="gameCount"></div>
|
|
922
349
|
</div>
|
|
923
|
-
<
|
|
924
|
-
<select id="cz-bgtype" class="select-mini">
|
|
925
|
-
<option value="aurora">aurora · animated</option>
|
|
926
|
-
<option value="grid">glow grid</option>
|
|
927
|
-
<option value="stars">night sky</option>
|
|
928
|
-
<option value="mesh">static mesh</option>
|
|
929
|
-
<option value="custom">custom image</option>
|
|
930
|
-
</select>
|
|
931
|
-
<div class="cz-row hidden" id="cz-bgurl-row">
|
|
932
|
-
<input id="cz-bgurl" class="search-input" type="text" placeholder="https://… background image URL">
|
|
933
|
-
</div>
|
|
934
|
-
<label class="cz-label" for="cz-bg-hue">background hue</label>
|
|
935
|
-
<input type="range" id="cz-bg-hue" min="0" max="360" value="28">
|
|
936
|
-
<label class="cz-label" for="cz-glow">glow intensity</label>
|
|
937
|
-
<input type="range" id="cz-glow" min="0" max="100" value="70">
|
|
938
|
-
<label class="cz-label" for="cz-blur">card blur</label>
|
|
939
|
-
<input type="range" id="cz-blur" min="0" max="30" value="14">
|
|
940
|
-
<label class="cz-label" for="cz-radius">card radius</label>
|
|
941
|
-
<input type="range" id="cz-radius" min="4" max="32" value="18">
|
|
942
|
-
<label class="cz-label" for="cz-font">font</label>
|
|
943
|
-
<select id="cz-font" class="select-mini">
|
|
944
|
-
<option value="outfit">Outfit</option>
|
|
945
|
-
<option value="grotesk">Space Grotesk</option>
|
|
946
|
-
<option value="mono">JetBrains Mono</option>
|
|
947
|
-
<option value="system">System</option>
|
|
948
|
-
</select>
|
|
949
|
-
<label class="cz-label">effects</label>
|
|
950
|
-
<div class="cz-toggles">
|
|
951
|
-
<label class="cz-toggle"><input type="checkbox" id="cz-particles" checked><span>particles</span></label>
|
|
952
|
-
<label class="cz-toggle"><input type="checkbox" id="cz-grid" checked><span>grid</span></label>
|
|
953
|
-
<label class="cz-toggle"><input type="checkbox" id="cz-noise" checked><span>noise</span></label>
|
|
954
|
-
<label class="cz-toggle"><input type="checkbox" id="cz-anim" checked><span>animations</span></label>
|
|
955
|
-
<label class="cz-toggle"><input type="checkbox" id="cz-sound"><span>sound fx</span></label>
|
|
956
|
-
<label class="cz-toggle"><input type="checkbox" id="cz-work"><span>work mode (H)</span></label>
|
|
957
|
-
</div>
|
|
958
|
-
<input id="cz-name" class="search-input" type="text" maxlength="20" placeholder="DRIVER">
|
|
959
|
-
<button class="btn ghost btn-block" id="cz-reset">reset everything</button>
|
|
960
|
-
<p class="cz-note">everything saves to your browser automatically</p>
|
|
961
|
-
</div>
|
|
962
|
-
</aside>
|
|
963
|
-
<!-- modals -->
|
|
964
|
-
<div class="modal hidden" id="launcher-modal" role="dialog" aria-modal="true">
|
|
965
|
-
<div class="launcher-bar">
|
|
966
|
-
<button class="icon-btn" id="launcher-back">←</button>
|
|
967
|
-
<span class="launcher-title" id="launcher-title">—</span>
|
|
968
|
-
<div class="launcher-actions">
|
|
969
|
-
<button class="btn ghost btn-sm" id="launcher-refresh">⟳</button>
|
|
970
|
-
<button class="btn ghost btn-sm" id="launcher-fs">⚬ fullscreen</button>
|
|
971
|
-
<button class="icon-btn" id="launcher-close">✕</button>
|
|
972
|
-
</div>
|
|
973
|
-
</div>
|
|
974
|
-
<div class="launcher-frame-wrap">
|
|
975
|
-
<iframe id="launcher-frame" sandbox="allow-scripts allow-same-origin allow-popups allow-presentation allow-pointer-lock allow-forms allow-modals allow-downloads allow-top-navigation-by-user-activation"></iframe>
|
|
350
|
+
<div class="games-grid" id="gamesGrid"></div>
|
|
976
351
|
</div>
|
|
977
352
|
</div>
|
|
978
353
|
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
<
|
|
983
|
-
|
|
984
|
-
<
|
|
985
|
-
<button class="btn ghost btn-sm" id="player-mark">✓ watched</button>
|
|
986
|
-
<button class="icon-btn" id="player-close">✕</button>
|
|
354
|
+
<!-- ============ MOVIE HUB ============ -->
|
|
355
|
+
<div class="screen" id="movies">
|
|
356
|
+
<div class="hub">
|
|
357
|
+
<div class="hub-head">
|
|
358
|
+
<h2>Movie <em>Theater</em></h2>
|
|
359
|
+
<div class="count" id="movieCount"></div>
|
|
987
360
|
</div>
|
|
361
|
+
<div class="movies-grid" id="moviesGrid"></div>
|
|
988
362
|
</div>
|
|
989
|
-
<div class="player-stage" id="player-stage"></div>
|
|
990
363
|
</div>
|
|
991
364
|
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
<div class="
|
|
996
|
-
<
|
|
997
|
-
<
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
<input id="form-desc" type="text" maxlength="200">
|
|
365
|
+
<!-- ============ AI ============ -->
|
|
366
|
+
<div class="screen" id="ai">
|
|
367
|
+
<div class="ai-wrap">
|
|
368
|
+
<div class="ai-head">
|
|
369
|
+
<div class="ai-orb"></div>
|
|
370
|
+
<div>
|
|
371
|
+
<h2>IGNITE <span style="color:var(--accent)">AI</span></h2>
|
|
372
|
+
<p>Super-smart assistant · great at coding, answers & ideas</p>
|
|
373
|
+
</div>
|
|
1002
374
|
</div>
|
|
1003
|
-
<div
|
|
1004
|
-
|
|
1005
|
-
<
|
|
375
|
+
<div id="chatLog"></div>
|
|
376
|
+
<div class="chips" id="chips">
|
|
377
|
+
<button class="chip" onclick="quickAsk('Write a Python script that sorts a list and explain it')">💻 Give me code</button>
|
|
378
|
+
<button class="chip" onclick="quickAsk('Explain quantum computing simply')">🧠 Teach me something</button>
|
|
379
|
+
<button class="chip" onclick="quickAsk('Help me build a game in JavaScript')">🎮 Build a game</button>
|
|
1006
380
|
</div>
|
|
1007
|
-
<div class="
|
|
1008
|
-
<
|
|
1009
|
-
<
|
|
381
|
+
<div id="aiInputBar" class="glass">
|
|
382
|
+
<input id="aiInput" placeholder="Ask IGNITE AI anything… even code" autocomplete="off">
|
|
383
|
+
<button id="sendBtn" onclick="sendMsg()">➤</button>
|
|
1010
384
|
</div>
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
385
|
+
</div>
|
|
386
|
+
</div>
|
|
387
|
+
|
|
388
|
+
<!-- ============ DOCK ============ -->
|
|
389
|
+
<nav id="dock" class="glass">
|
|
390
|
+
<button class="dock-btn on" data-s="home" onclick="go('home')"><span class="dicon">⌂</span>Home</button>
|
|
391
|
+
<button class="dock-btn" data-s="games" onclick="go('games')"><span class="dicon">🎮</span>Games</button>
|
|
392
|
+
<button class="dock-btn" data-s="movies" onclick="go('movies')"><span class="dicon">🎬</span>Movies</button>
|
|
393
|
+
<button class="dock-btn" data-s="ai" onclick="go('ai')"><span class="dicon">✦</span>AI</button>
|
|
394
|
+
<button class="dock-btn" onclick="openSettings()"><span class="dicon">⚙</span></button>
|
|
395
|
+
</nav>
|
|
396
|
+
|
|
397
|
+
<!-- ============ PLAY MODAL ============ -->
|
|
398
|
+
<div id="playModal">
|
|
399
|
+
<div class="play-shell">
|
|
400
|
+
<div class="play-bar">
|
|
401
|
+
<span class="live"></span>
|
|
402
|
+
<span class="ttl" id="playTitle">Now Playing</span>
|
|
403
|
+
<button onclick="closePlay()">✕ Close</button>
|
|
1014
404
|
</div>
|
|
1015
|
-
<div
|
|
1016
|
-
<
|
|
1017
|
-
<
|
|
405
|
+
<div style="position:relative;flex:1;display:flex">
|
|
406
|
+
<div class="play-loading" id="playLoading"><div class="spinner"></div><div style="color:#888;font-size:13px">Loading…</div></div>
|
|
407
|
+
<iframe id="playFrame" allowfullscreen sandbox="allow-scripts allow-same-origin allow-forms allow-popups-to-escape-sandbox"></iframe>
|
|
1018
408
|
</div>
|
|
1019
|
-
|
|
1020
|
-
</form>
|
|
409
|
+
</div>
|
|
1021
410
|
</div>
|
|
1022
411
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
<
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
IGNITE OS — data layer
|
|
1029
|
-
Games, theme presets and demo movies. The default games all play
|
|
1030
|
-
through the IGNITE OS proxy (/go/ form) or straight from /lg/ (local).
|
|
1031
|
-
Want more games? Just append entries here.
|
|
1032
|
-
═══════════════════════════════════════════════════════════════════ */
|
|
1033
|
-
|
|
1034
|
-
const THEME_PRESETS = {
|
|
1035
|
-
ember: { accent: '#ff6b1a', accent2: '#ffb26b', bgHue: 28, bgType: 'aurora' },
|
|
1036
|
-
cobalt: { accent: '#3b82f6', accent2: '#93c5fd', bgHue: 205, bgType: 'aurora' },
|
|
1037
|
-
violet: { accent: '#a855f7', accent2: '#d8b4fe', bgHue: 268, bgType: 'aurora' },
|
|
1038
|
-
inferno: { accent: '#ff3d3d', accent2: '#ffb347', bgHue: 4, bgType: 'grid' },
|
|
1039
|
-
ocean: { accent: '#22d3ee', accent2: '#a5f3fc', bgHue: 188, bgType: 'aurora' },
|
|
1040
|
-
mint: { accent: '#34d399', accent2: '#a7f3d0', bgHue: 150, bgType: 'aurora' },
|
|
1041
|
-
};
|
|
1042
|
-
|
|
1043
|
-
const DEFAULT_GAMES = [
|
|
1044
|
-
{
|
|
1045
|
-
id: 'Cookie Clicker', name: 'Cookie Clicker TEST',
|
|
1046
|
-
url: 'https://unpkg.com/edcube-cookie-clicker@5.0.0/index.html',
|
|
1047
|
-
cover: 'data:image/jpeg;base64,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',// ← optional image URL — letters disappear when set
|
|
1048
|
-
desc: 'cool',
|
|
1049
|
-
tags: ['cookie', '2D'], color: '#22d3ee',
|
|
1050
|
-
},
|
|
1051
|
-
{
|
|
1052
|
-
id: 'recoil', name: 'Recoil',
|
|
1053
|
-
url: 'https://generic-alpaca-325321.framer.app/',
|
|
1054
|
-
cover: 'https://fancade.com/images/5E9F40DA7F9C9FA8.jpg', // ← optional image URL — letters disappear when set
|
|
1055
|
-
desc: 'recoil like drivve mad',
|
|
1056
|
-
tags: ['closed-world', 'action', '2D'], color: '#ff6b1a',
|
|
1057
|
-
},
|
|
1058
|
-
{
|
|
1059
|
-
id: 'retro ball', name: 'Retro Ball',
|
|
1060
|
-
url: 'https://objectstorage.us-ashburn-1.oraclecloud.com/n/idzgyfi7awwr/b/bucket-20231001-1518-rb1.0/o/index.html',
|
|
1061
|
-
cover: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTfQEUbXopS95V30bru2UdgdystMD8ElzCDFzH6k6_pUQ&s', // ← optional image URL — letters disappear when set
|
|
1062
|
-
desc: 'touchdown',
|
|
1063
|
-
tags: ['sports', 'action'], color: '#a855f7',
|
|
1064
|
-
},
|
|
1065
|
-
{
|
|
1066
|
-
id: 'drift boss', name: 'Drift Boss',
|
|
1067
|
-
url: 'https://www.mathplayground.com/drift-boss-v3/index.html',
|
|
1068
|
-
cover: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTMW_L-ZhbnvWj1lwNrWeAGDaK0rG0lCw52zSIsTfTmWNZ9oOVhXvQ_oJRA&s=10', // ← optional image URL — letters disappear when set
|
|
1069
|
-
desc: 'drive',
|
|
1070
|
-
tags: ['car', 'arcade', '2D'], color: '#3b82f6',
|
|
1071
|
-
},
|
|
1072
|
-
{
|
|
1073
|
-
id: 'tiny fishing', name: 'Tiny fishing',
|
|
1074
|
-
url: 'https://generic-alpaca-325321.framer.app/page-3',
|
|
1075
|
-
cover: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR-I2lTQx2HkSPrPeyCNmAnGGd_c29Um34tPc8TpiyJjg&s=10', // ← optional image URL — letters disappear when set
|
|
1076
|
-
desc: 'works trust',
|
|
1077
|
-
tags: ['slow but works', '3D'], color: '#facc15',
|
|
1078
|
-
},
|
|
1079
|
-
{
|
|
1080
|
-
id: 'A Small world Cup', name: 'A Small World Cup',
|
|
1081
|
-
url: 'https://script.google.com/a/macros/mcpsmd.net/s/AKfycbwSK5IqLXbZppMz9znIJVycB4BZCHodE3uSgUF4C1fReEqmMSDYua81Ew7Rp-Cn85gojg/exec',
|
|
1082
|
-
cover: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTY4AGQkfiAEPBItQdxMlfKm0Wz7eUOzt-SsZUGejt7cA&s=10', // ← optional image URL — letters disappear when set
|
|
1083
|
-
desc: 'Soccer Skills — Euro 2021 edition. Curl it into the top corner.',
|
|
1084
|
-
tags: ['soccer', 'sports'], color: '#34d399',
|
|
1085
|
-
},
|
|
1086
|
-
{
|
|
1087
|
-
id: 'Air Hockey CUP', name: 'Air Hockey CUP',
|
|
1088
|
-
url: 'https://script.google.com/macros/s/AKfycbw7VnhYKWITlJo9p8qyT8ExOfvnjvizq57_jj7MzDnyJWTuHtNbee3NHoEoRhuS6eqo/exec',
|
|
1089
|
-
cover: 'https://play-lh.googleusercontent.com/EFevQ0zkoUL5FEPIsI8-yFKoK6QCyu3TSZRXEJiLkRaWrCidW3b6ZKNiNzuvfeaLKW9FyHeY3WYh4RR839Qanck', // ← optional image URL — letters disappear when set
|
|
1090
|
-
desc: 'basic hockey',
|
|
1091
|
-
tags: ['hockey', 'sports'], color: '#f43f5e',
|
|
1092
|
-
},
|
|
1093
|
-
{
|
|
1094
|
-
id: 'Slope REMAKE', name: 'Slope REMAKE',
|
|
1095
|
-
url: 'https://script.google.com/a/macros/mcpsmd.net/s/AKfycbyendSzfWzjivGUAoNgH5QDpJIxJDyJvSYwXWxcV_8gl9xkdTMV2BvDpjiWNw5pGaVT/exec',
|
|
1096
|
-
cover: 'https://play-lh.googleusercontent.com/Ev5gkPTONr-1k1r7erT0iiW-pUZoGUH_6FVQdD91e9ghSAqZRMTaFVGQW5Z6PLOfFgxZnJhdEBE2z-TgfZ1bsg', // ← optional image URL — letters disappear when set
|
|
1097
|
-
desc: 'a rolling ball ',
|
|
1098
|
-
tags: ['slope','3D'], color: '#fb7185',
|
|
1099
|
-
},
|
|
1100
|
-
{
|
|
1101
|
-
id: 'Subway Surfers', name: 'Subway Surfers',
|
|
1102
|
-
url: 'https://unpkg.com/subwaystpetersburg@1.0.0/index.html',
|
|
1103
|
-
cover: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTzwmhMeCmyvXSpjVLHUUCok1FZhyrP-D9klmlq8Pt-Ww&s=10', // ← optional image URL — letters disappear when set
|
|
1104
|
-
desc: 'run from cop ',
|
|
1105
|
-
tags: ['endless','cop'], color: '#fb7185',
|
|
1106
|
-
},
|
|
1107
|
-
{
|
|
1108
|
-
id: 'Bacon May Die', name: 'Bacon May Die',
|
|
1109
|
-
url: 'https://unpkg.com/schooluk-bacon-may-die@1.0.0/index.html',
|
|
1110
|
-
cover: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRG8Ra7wjHyaMeLnHp70ql98oi25b33UsPI63N-Jwbapw&s=10',
|
|
1111
|
-
desc: 'brawler — fight your way through.',
|
|
1112
|
-
tags: ['brawler','action'], color: '#fb7185',
|
|
1113
|
-
},
|
|
1114
|
-
{
|
|
1115
|
-
id: 'paper-fly', name: 'Paper Fly',
|
|
1116
|
-
url: 'https://unpkg.com/papergmfly@1.0.0/index.html',
|
|
1117
|
-
cover: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQQFa7pNRwbmpuk0rwFYmYYjwAdFMI6qH18R1rO_rd8XQ&s=10',
|
|
1118
|
-
desc: 'fold, flick and fly.',
|
|
1119
|
-
tags: ['arcade','2D'], color: '#22d3ee',
|
|
1120
|
-
},
|
|
1121
|
-
{
|
|
1122
|
-
id: 'slope-505', name: 'Slope 505',
|
|
1123
|
-
url: 'https://unpkg.com/sloeplus@1.0.0/index.html',
|
|
1124
|
-
cover: 'https://play-lh.googleusercontent.com/Ev5gkPTONr-1k1r7erT0iiW-pUZoGUH_6FVQdD91e9ghSAqZRMTaFVGQW5Z6PLOfFgxZnJhdEBE2z-TgfZ1bsg',
|
|
1125
|
-
desc: 'the rolling ball, remade.',
|
|
1126
|
-
tags: ['slope','3D'], color: '#fb7185',
|
|
1127
|
-
},
|
|
1128
|
-
{
|
|
1129
|
-
id: 'boat-game', name: 'Boat Game',
|
|
1130
|
-
url: 'https://unpkg.com/boateman@1.0.0/index.html',
|
|
1131
|
-
cover: 'https://play-lh.googleusercontent.com/k80-V4CxUpsoNUTiZCasfAF4yTxy428anUQiM_AieHnLRMq2qmCPIHIYWT8dYATGyo1eTjZLZEa3aa3b4kyingA',
|
|
1132
|
-
desc: 'captain your boat.',
|
|
1133
|
-
tags: ['boat','arcade'], color: '#3b82f6',
|
|
1134
|
-
},
|
|
1135
|
-
{
|
|
1136
|
-
id: '1v1-lol', name: '1v1.lol',
|
|
1137
|
-
url: 'https://unpkg.com/edcube-1v1-lol@5.0.0/index.html',
|
|
1138
|
-
cover: 'https://play-lh.googleusercontent.com/NAE7nd8mxERLlT-eIM68aEQi1RthPUzVd8KN_C-RPmvDgBLJ-C_kBHrBQFYPi8wQwr_nkxGUadoS0xStbU_jc7s=w240-h480-rw',
|
|
1139
|
-
desc: 'build and battle, 1v1.',
|
|
1140
|
-
tags: ['shooter','multiplayer'], color: '#22d3ee',
|
|
1141
|
-
},
|
|
1142
|
-
{
|
|
1143
|
-
id: 'crossy-road', name: 'Crossy Road',
|
|
1144
|
-
url: 'https://unpkg.com/edcube-crossy-road@5.0.0/index.html',
|
|
1145
|
-
cover: 'https://play-lh.googleusercontent.com/LX__Y8tKCvkvEgboZtQfDsvYiNFa6kwWSpqObipZI2MLK5rFVd5tEU8xgMNoMj1ICOf9TO7PFoFmiKGeIpf1=w240-h480-rw',
|
|
1146
|
-
desc: 'cross the road, dodge the traffic.',
|
|
1147
|
-
tags: ['arcade','crossing'], color: '#facc15',
|
|
1148
|
-
},
|
|
1149
|
-
{
|
|
1150
|
-
id: 'basketball-stars', name: 'Basketball Stars',
|
|
1151
|
-
url: 'https://unpkg.com/edcube-basketball-stars@5.0.0/index.html',
|
|
1152
|
-
cover: 'https://m.media-amazon.com/images/I/81BL1Hx2KuL.png',
|
|
1153
|
-
desc: 'sink threes and take the crown.',
|
|
1154
|
-
tags: ['sports','basketball'], color: '#ff6b1a',
|
|
1155
|
-
},
|
|
1156
|
-
{
|
|
1157
|
-
id: 'bitlife', name: 'Bitlife',
|
|
1158
|
-
url: 'https://unpkg.com/edcube-bitlife@5.0.0/index.html',
|
|
1159
|
-
cover: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRZdo5cBqkv1uAcfCofpC-WAViocRNLlysohwHMV2Lqzw&s=10',
|
|
1160
|
-
desc: 'live a whole life, one tap at a time.',
|
|
1161
|
-
tags: ['sim','life'], color: '#34d399',
|
|
1162
|
-
},
|
|
1163
|
-
{
|
|
1164
|
-
id: 'tag', name: 'Tag',
|
|
1165
|
-
url: 'https://unpkg.com/edcube-tag@1.0.0/index.html',
|
|
1166
|
-
cover: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSFwGQ9vG6ZifxDpWHtuu8oSzYqll79xZ9rW_KwPwzCdA&s',
|
|
1167
|
-
desc: "don't get caught.",
|
|
1168
|
-
tags: ['platformer','2D'], color: '#facc15',
|
|
1169
|
-
},
|
|
1170
|
-
{
|
|
1171
|
-
id: 'block-blast', name: 'Block Blast',
|
|
1172
|
-
url: 'https://unpkg.com/edcube-block-blast@5.0.0/index.html',
|
|
1173
|
-
cover: 'https://imgs.crazygames.com/block-puzzle-master_1x1/20250924025352/block-puzzle-master_1x1-cover?format=auto&quality=70&metadata=none',
|
|
1174
|
-
desc: 'blast blocks, clear lines.',
|
|
1175
|
-
tags: ['puzzle'], color: '#a855f7',
|
|
1176
|
-
},
|
|
1177
|
-
{
|
|
1178
|
-
id: 'cut-the-rope', name: 'Cut the Rope',
|
|
1179
|
-
url: 'https://unpkg.com/edcube-cut-the-rope@5.0.0/index.html',
|
|
1180
|
-
cover: 'https://play-lh.googleusercontent.com/2bTmHHM7DBMVe7fLh16Em-J-tVBMFGL2HJCxPAMNF7gAYIfU6EZoYG1__lQ0cvFMEyp3GU9CGijZzEB1qJHF',
|
|
1181
|
-
desc: 'feed the monster, cut the rope.',
|
|
1182
|
-
tags: ['puzzle'], color: '#34d399',
|
|
1183
|
-
},
|
|
1184
|
-
];
|
|
1185
|
-
|
|
1186
|
-
const DEMO_MOVIES = [
|
|
1187
|
-
{ title: 'Spiderman: Far from home ', genre: 'Spiderman · Open Movie', desc: 'spiderman goes in new york idk',
|
|
1188
|
-
source: 'url', videoUrl: 'https://drive.google.com/file/d/1t62YY5V2WFqPA801cDECww1EQYk4fMnk/preview?t=11.299',
|
|
1189
|
-
poster: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSjdXbHSbJUSaJdgj45vDisJUiPj1qljxkOPbfojU3M1w&s=10' },
|
|
1190
|
-
{ title: 'Spiderman: Home comming', genre: 'spider · Open Movie', desc: 'wtf.',
|
|
1191
|
-
source: 'url', videoUrl: 'https://drive.google.com/file/d/1hUIx05G3N_ucg8SwdZHRt_5MdqLQZtai/preview?t=6',
|
|
1192
|
-
poster: 'https://resizing.flixster.com/-XZAfHZM39UwaGJIFWKAE8fS0ak=/v3/t/assets/p12798844_p_v8_ao.jpg' },
|
|
1193
|
-
{ title: 'F1', genre: ' Open Movie', desc: 'idk anything about it.',
|
|
1194
|
-
source: 'url', videoUrl: 'https://drive.google.com/file/d/1k_zRDt4Rn_9FotYJUfrhJZ0ZDKfwqQk1/preview',
|
|
1195
|
-
poster: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSr2SgmDLkFhLGuF1CnP_xJv1stiKvK027DkN6m6wdqdg&s=10' },
|
|
1196
|
-
{ title: 'OBBSESSION', genre: ' Open Movie', desc: 'idk this movie either',
|
|
1197
|
-
source: 'url', videoUrl: 'https://drive.google.com/file/d/1FEN16LwM5ZzTBSbBtkY95LrIzlsHXFx7/preview',
|
|
1198
|
-
poster: 'https://images.contentstack.io/v3/assets/blt13adb7e2033fcee5/blt5c8bcbfee5dade5c/6a2b47a8c284bac540ef3353/Obsession_keyart_poster_2000x3000.jpg?width=2560' },
|
|
1199
|
-
];
|
|
1200
|
-
|
|
1201
|
-
/* IGNITE OS app — embedded */
|
|
1202
|
-
/* ═══════════════════════════════════════════════════════════════
|
|
1203
|
-
IGNITE OS — dashboard engine (zero deps)
|
|
1204
|
-
═══════════════════════════════════════════════════════════════ */
|
|
1205
|
-
(function () {
|
|
1206
|
-
'use strict';
|
|
1207
|
-
|
|
1208
|
-
const $ = (id) => document.getElementById(id);
|
|
1209
|
-
|
|
1210
|
-
// ── persistence ──
|
|
1211
|
-
const K = {
|
|
1212
|
-
games: 'emby.games.v1', movies: 'emby.movies.v2',
|
|
1213
|
-
prefs: 'emby.prefs.v1', profile: 'emby.profile.v1',
|
|
1214
|
-
last: 'emby.last.v1', overrides: 'emby.overrides.v1',
|
|
1215
|
-
};
|
|
1216
|
-
const store = {
|
|
1217
|
-
get(key, fallback) {
|
|
1218
|
-
try { const v = JSON.parse(localStorage.getItem(key)); return v === null || v === undefined ? fallback : v; }
|
|
1219
|
-
catch { return fallback; }
|
|
1220
|
-
},
|
|
1221
|
-
set(key, val) {
|
|
1222
|
-
try { localStorage.setItem(key, JSON.stringify(val)); } catch { /* ignore */ }
|
|
1223
|
-
},
|
|
1224
|
-
};
|
|
412
|
+
<!-- ============ SETTINGS ============ -->
|
|
413
|
+
<div id="settingsModal" onclick="if(event.target===this)closeSettings()">
|
|
414
|
+
<div class="settings-card glass">
|
|
415
|
+
<h3>Settings</h3>
|
|
416
|
+
<div class="sub">Customize IGNITE OS — accent color, weather & particles.</div>
|
|
1225
417
|
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
movies: store.get(K.movies, []),
|
|
1231
|
-
overrides: store.get(K.overrides, {}),
|
|
1232
|
-
prefs: Object.assign({
|
|
1233
|
-
preset: 'ember', accent: '#ff6b1a', accent2: '#ffb26b',
|
|
1234
|
-
bgType: 'aurora', bgHue: 28, bgUrl: '', glow: 70, blur: 14,
|
|
1235
|
-
radius: 18, font: 'outfit', particles: true, grid: true,
|
|
1236
|
-
noise: true, anim: true, sound: false, work: false,
|
|
1237
|
-
}, store.get(K.prefs, {})),
|
|
1238
|
-
profile: Object.assign({ name: 'DRIVER' }, store.get(K.profile, {})),
|
|
1239
|
-
movieFilter: '', movieGenre: '', movieSort: 'added',
|
|
1240
|
-
playerMovie: null, playerEngine: 'video',
|
|
1241
|
-
launcherUrl: '', formEditId: null, formMode: 'movie',
|
|
1242
|
-
keys: [], turbo: false,
|
|
1243
|
-
};
|
|
1244
|
-
|
|
1245
|
-
// ── proxy url helpers ──
|
|
1246
|
-
const serverBase = () => {
|
|
1247
|
-
try { return String(localStorage.getItem('emby.server') || '').replace(/\/+$/, ''); }
|
|
1248
|
-
catch { return ''; }
|
|
1249
|
-
};
|
|
1250
|
-
const proxify = (raw) => {
|
|
1251
|
-
let u = (raw || '').trim();
|
|
1252
|
-
if (!u) return '';
|
|
1253
|
-
if (!/^[a-z][a-z0-9+.-]*:/i.test(u)) u = 'https://' + u;
|
|
1254
|
-
return u;
|
|
1255
|
-
};
|
|
1256
|
-
const tunnel = (raw) => proxify(raw, '/go/');
|
|
1257
|
-
|
|
1258
|
-
// ── google drive parser ──
|
|
1259
|
-
const DRIVE_ID = /[A-Za-z0-9_-]{28,}/;
|
|
1260
|
-
function parseDrive(input) {
|
|
1261
|
-
const s = (input || '').trim();
|
|
1262
|
-
if (!s) return { kind: 'none' };
|
|
1263
|
-
if (/^[A-Za-z0-9_-]{28,}$/.test(s)) return { kind: 'file', id: s };
|
|
1264
|
-
let u;
|
|
1265
|
-
try { u = new URL(s); } catch { return { kind: 'url', url: s }; }
|
|
1266
|
-
const host = u.hostname.toLowerCase();
|
|
1267
|
-
if (host === 'drive.google.com' || host === 'drive.usercontent.google.com' || host === 'docs.google.com') {
|
|
1268
|
-
const path = u.pathname;
|
|
1269
|
-
const mFile = path.match(/\/file\/d\/([^/]+)/);
|
|
1270
|
-
const mFolder = path.match(/\/drive\/folders\/([^/]+)/);
|
|
1271
|
-
const idQ = u.searchParams.get('id');
|
|
1272
|
-
const folderQ = u.searchParams.get('folder');
|
|
1273
|
-
if (mFolder || folderQ) return { kind: 'folder', id: (mFolder ? mFolder[1] : folderQ) };
|
|
1274
|
-
if (mFile) return { kind: 'file', id: mFile[1] };
|
|
1275
|
-
if (idQ) return { kind: 'file', id: idQ };
|
|
1276
|
-
if (DRIVE_ID.test(path)) return { kind: 'file', id: path.match(DRIVE_ID)[0] };
|
|
1277
|
-
}
|
|
1278
|
-
return { kind: 'url', url: s };
|
|
1279
|
-
}
|
|
1280
|
-
const drivePreview = (id) => 'https://drive.google.com/file/d/' + id + '/preview';
|
|
1281
|
-
const driveRaw = (id) => 'https://drive.usercontent.google.com/download?id=' + id + '&export=download&confirm=t';
|
|
1282
|
-
const driveFolder = (id) => 'https://drive.google.com/embeddedfolderview?id=' + id + '#list';
|
|
1283
|
-
const driveThumb = (id) => 'https://drive.google.com/thumbnail?id=' + id + '&sz=w1000';
|
|
418
|
+
<div class="set-row">
|
|
419
|
+
<div><div class="lbl">Accent Color</div><div class="hint">The tiny bit of orange — make it yours.</div></div>
|
|
420
|
+
<div class="swatches" id="swatches"></div>
|
|
421
|
+
</div>
|
|
1284
422
|
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
t.className = 'toast'; t.textContent = msg;
|
|
1290
|
-
zone.appendChild(t);
|
|
1291
|
-
setTimeout(() => { t.classList.add('out'); setTimeout(() => t.remove(), 400); }, ms || 2600);
|
|
1292
|
-
}
|
|
423
|
+
<div class="set-row">
|
|
424
|
+
<div><div class="lbl">Weather City</div><div class="hint">Leave blank to use your location.</div></div>
|
|
425
|
+
<input class="set-input" id="cityInput" type="text" placeholder="e.g. New York">
|
|
426
|
+
</div>
|
|
1293
427
|
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
try {
|
|
1299
|
-
audioCtx = audioCtx || new (window.AudioContext || window.webkitAudioContext)();
|
|
1300
|
-
const o = audioCtx.createOscillator(), g = audioCtx.createGain();
|
|
1301
|
-
o.type = type || 'sine'; o.frequency.value = freq;
|
|
1302
|
-
g.gain.setValueAtTime(0.06, audioCtx.currentTime);
|
|
1303
|
-
g.gain.exponentialRampToValueAtTime(0.0001, audioCtx.currentTime + (dur || 0.12));
|
|
1304
|
-
o.connect(g); g.connect(audioCtx.destination);
|
|
1305
|
-
o.start(); o.stop(audioCtx.currentTime + (dur || 0.12));
|
|
1306
|
-
} catch { /* ignore */ }
|
|
1307
|
-
}
|
|
428
|
+
<div class="set-row">
|
|
429
|
+
<div><div class="lbl">Particle Density</div><div class="hint">Snow intensity on the home screen.</div></div>
|
|
430
|
+
<input type="range" id="particleRange" min="40" max="260" step="10">
|
|
431
|
+
</div>
|
|
1308
432
|
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
const p = document.createElement('i');
|
|
1314
|
-
p.className = 'confetti-piece';
|
|
1315
|
-
p.style.left = Math.random() * 100 + 'vw';
|
|
1316
|
-
p.style.background = colors[(Math.random() * colors.length) | 0];
|
|
1317
|
-
p.style.animationDuration = (1.6 + Math.random() * 1.8) + 's';
|
|
1318
|
-
p.style.animationDelay = (Math.random() * 0.35) + 's';
|
|
1319
|
-
document.body.appendChild(p);
|
|
1320
|
-
setTimeout(() => p.remove(), 4200);
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
433
|
+
<div class="set-row">
|
|
434
|
+
<div><div class="lbl">AI Model</div><div class="hint">Switch the brain behind IGNITE AI.</div></div>
|
|
435
|
+
<input class="set-input" id="modelInput" type="text" placeholder="model name">
|
|
436
|
+
</div>
|
|
1323
437
|
|
|
1324
|
-
|
|
1325
|
-
|
|
438
|
+
<div style="display:flex;gap:12px;margin-top:26px">
|
|
439
|
+
<button class="nav-btn primary glass" style="flex:1;justify-content:center" onclick="saveSettings()">Save</button>
|
|
440
|
+
<button class="nav-btn glass" style="flex:1;justify-content:center" onclick="closeSettings()">Cancel</button>
|
|
441
|
+
</div>
|
|
442
|
+
</div>
|
|
443
|
+
</div>
|
|
1326
444
|
|
|
1327
|
-
|
|
1328
|
-
const h = hex.replace('#', '');
|
|
1329
|
-
const n = parseInt(h.length === 3 ? h.split('').map((c) => c + c).join('') : h, 16);
|
|
1330
|
-
return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 };
|
|
1331
|
-
};
|
|
445
|
+
<div id="toast"></div>
|
|
1332
446
|
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
447
|
+
<script>
|
|
448
|
+
/* ================= DATA (links encoded — not exposed) ================= */
|
|
449
|
+
const GAMES = [{id:'duck-math',name:'DUCK MATH',color:'#ffb26b',featured:true,u:'aHR0cHM6Ly91bnBrZy5jb20vaWduaXRlLWdvb2JlckAxLjEuNC9pbmRleC5odG1sIy9jbGFzcy9kcmlmdC1raW5n',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRJFXWvY3bIWxbR2SZEnbZaVFRYqFzZJZlOo4SYkWOexg&s'},
|
|
450
|
+
{id:'cookie-clicker',name:'Cookie Clicker',color:'#22d3ee',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vZWRjdWJlLWNvb2tpZS1jbGlja2VyQDUuMC4wL2luZGV4Lmh0bWw=',cover:'https://i.imgur.com/3ZQ3Z3Z.png'},
|
|
451
|
+
{id:'recoil',name:'Recoil',color:'#ff6b1a',featured:false,u:'aHR0cHM6Ly9nZW5lcmljLWFscGFjYS0zMjUzMjEuZnJhbWVyLmFwcC8=',cover:'https://fancade.com/images/5E9F40DA7F9C9FA8.jpg'},
|
|
452
|
+
{id:'retro-ball',name:'Retro Ball',color:'#a855f7',featured:false,u:'aHR0cHM6Ly9vYmplY3RzdG9yYWdlLnVzLWFzaGJ1cm4tMS5vcmFjbGVjbG91ZC5jb20vbi9pZHpneWZpN2F3d3IvYi9idWNrZXQtMjAyMzEwMDEtMTUxOC1yYjEuMC9vL2luZGV4Lmh0bWw=',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTfQEUbXopS95V30bru2UdgdystMD8ElzCDFzH6k6_pUQ&s'},
|
|
453
|
+
{id:'drift-boss',name:'Drift Boss',color:'#3b82f6',featured:false,u:'aHR0cHM6Ly93d3cubWF0aHBsYXlncm91bmQuY29tL2RyaWZ0LWJvc3MtdjMvaW5kZXguaHRtbA==',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTMW_L-ZhbnvWj1lwNrWeAGDaK0rG0lCw52zSIsTfTmWNZ9oOVhXvQ_oJRA&s=10'},
|
|
454
|
+
{id:'tiny-fishing',name:'Tiny Fishing',color:'#facc15',featured:false,u:'aHR0cHM6Ly9nZW5lcmljLWFscGFjYS0zMjUzMjEuZnJhbWVyLmFwcC9wYWdlLTM=',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR-I2lTQx2HkSPrPeyCNmAnGGd_c29Um34tPc8TpiyJjg&s=10'},
|
|
455
|
+
{id:'small-world-cup',name:'A Small World Cup',color:'#34d399',featured:false,u:'aHR0cHM6Ly9zY3JpcHQuZ29vZ2xlLmNvbS9hL21hY3Jvcy9tY3BzbWQubmV0L3MvQUtmeWNid1NLNUlxTFhiWnBwTXo5em5JSlZ5Y0I0QlpDSG9kRTN1U2dVRjRDMWZSZUVxbU1TRFl1YTgxRXc3UnAtQ244NWdvamcvZXhlYw==',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTY4AGQkfiAEPBItQdxMlfKm0Wz7eUOzt-SsZUGejt7cA&s=10'},
|
|
456
|
+
{id:'air-hockey',name:'Air Hockey CUP',color:'#f43f5e',featured:false,u:'aHR0cHM6Ly9zY3JpcHQuZ29vZ2xlLmNvbS9tYWNyb3Mvcy9BS2Z5Y2J3N1ZuaFlLV0lUbEpvOXA4cXlUOEV4T2Z2bmp2aXpxNTdfamo3TXpEbnlKV1R1SHROYmVlM05Ib0VvUmh1UzZlcW8vZXhlYw==',cover:'https://play-lh.googleusercontent.com/EFevQ0zkoUL5FEPIsI8-yFKoK6QCyu3TSZRXEJiLkRaWrCidW3b6ZKNiNzuvfeaLKW9FyHeY3WYh4RR839Qanck'},
|
|
457
|
+
{id:'slope-remake',name:'Slope REMAKE',color:'#fb7185',featured:false,u:'aHR0cHM6Ly9zY3JpcHQuZ29vZ2xlLmNvbS9hL21hY3Jvcy9tY3BzbWQubmV0L3MvQUtmeWNieWVuZFN6Zld6aml2R1VBb05nSDVRRHBKSXhKRHlKdlNZd1hXeGNWXzhnbDl4a2RUTVYyQnZEcGppV053NXBHYVZUL2V4ZWM=',cover:'https://play-lh.googleusercontent.com/Ev5gkPTONr-1k1r7erT0iiW-pUZoGUH_6FVQdD91e9ghSAqZRMTaFVGQW5Z6PLOfFgxZnJhdEBE2z-TgfZ1bsg'},
|
|
458
|
+
{id:'subway-surfers',name:'Subway Surfers',color:'#fb7185',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vc3Vid2F5c3RwZXRlcnNidXJnQDEuMC4wL2luZGV4Lmh0bWw=',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTzwmhMeCmyvXSpjVLHUUCok1FZhyrP-D9klmlq8Pt-Ww&s=10'},
|
|
459
|
+
{id:'bacon-may-die',name:'Bacon May Die',color:'#fb7185',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vc2Nob29sdWstYmFjb24tbWF5LWRpZUAxLjAuMC9pbmRleC5odG1s',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRG8Ra7wjHyaMeLnHp70ql98oi25b33UsPI63N-Jwbapw&s=10'},
|
|
460
|
+
{id:'paper-fly',name:'Paper Fly',color:'#22d3ee',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vcGFwZXJnbWZseUAxLjAuMC9pbmRleC5odG1s',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQQFa7pNRwbmpuk0rwFYmYYjwAdFMI6qH18R1rO_rd8XQ&s=10'},
|
|
461
|
+
{id:'slope-505',name:'Slope 505',color:'#fb7185',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vc2xvZXBsdXNAMS4wLjAvaW5kZXguaHRtbA==',cover:'https://play-lh.googleusercontent.com/Ev5gkPTONr-1k1r7erT0iiW-pUZoGUH_6FVQdD91e9ghSAqZRMTaFVGQW5Z6PLOfFgxZnJhdEBE2z-TgfZ1bsg'},
|
|
462
|
+
{id:'boat-game',name:'Boat Game',color:'#3b82f6',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vYm9hdGVtYW5AMS4wLjAvaW5kZXguaHRtbA==',cover:'https://play-lh.googleusercontent.com/k80-V4CxUpsoNUTiZCasfAF4yTxy428anUQiM_AieHnLRMq2qmCPIHIYWT8dYATGyo1eTjZLZEa3aa3b4kyingA'},
|
|
463
|
+
{id:'1v1-lol',name:'1v1.lol',color:'#22d3ee',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vZWRjdWJlLTF2MS1sb2xANS4wLjAvaW5kZXguaHRtbA==',cover:'https://play-lh.googleusercontent.com/NAE7nd8mxERLlT-eIM68aEQi1RthPUzVd8KN_C-RPmvDgBLJ-C_kBHrBQFYPi8wQwr_nkxGUadoS0xStbU_jc7s=w240-h480-rw'},
|
|
464
|
+
{id:'crossy-road',name:'Crossy Road',color:'#facc15',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vZWRjdWJlLWNyb3NzeS1yb2FkQDUuMC4wL2luZGV4Lmh0bWw=',cover:'https://play-lh.googleusercontent.com/LX__Y8tKCvkvEgboZtQfDsvYiNFa6kwWSpqObipZI2MLK5rFVd5tEU8xgMNoMj1ICOf9TO7PFoFmiKGeIpf1=w240-h480-rw'},
|
|
465
|
+
{id:'basketball-stars',name:'Basketball Stars',color:'#ff6b1a',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vZWRjdWJlLWJhc2tldGJhbGwtc3RhcnNANS4wLjAvaW5kZXguaHRtbA==',cover:'https://m.media-amazon.com/images/I/81BL1Hx2KuL.png'},
|
|
466
|
+
{id:'bitlife',name:'Bitlife',color:'#34d399',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vZWRjdWJlLWJpdGxpZmVANS4wLjAvaW5kZXguaHRtbA==',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRZdo5cBqkv1uAcfCofpC-WAViocRNLlysohwHMV2Lqzw&s=10'},
|
|
467
|
+
{id:'tag',name:'Tag',color:'#facc15',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vZWRjdWJlLXRhZ0AxLjAuMC9pbmRleC5odG1s',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSFwGQ9vG6ZifxDpWHtuu8oSzYqll79xZ9rW_KwPwzCdA&s'},
|
|
468
|
+
{id:'block-blast',name:'Block Blast',color:'#a855f7',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vZWRjdWJlLWJsb2NrLWJsYXN0QDUuMC4wL2luZGV4Lmh0bWw=',cover:'https://imgs.crazygames.com/block-puzzle-master_1x1/20250924025352/block-puzzle-master_1x1-cover?format=auto&quality=70&metadata=none'},
|
|
469
|
+
{id:'cut-the-rope',name:'Cut the Rope',color:'#34d399',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vZWRjdWJlLWN1dC10aGUtcm9wZUA1LjAuMC9pbmRleC5odG1s',cover:'https://play-lh.googleusercontent.com/2bTmHHM7DBMVe7fLh16Em-J-tVBMFGL2HJCxPAMNF7gAYIfU6EZoYG1__lQ0cvFMEyp3GU9CGijZzEB1qJHF'}];
|
|
470
|
+
const MOVIES = [{name:'Spiderman: Far From Home',u:'aHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2ZpbGUvZC8xdDYyWVk1VjJXRnFQQTgwMWNERUN3dzFFUVlrNGZNbmsvcHJldmlldw==',poster:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSjdXbHSbJUSaJdgj45vDisJUiPj1qljxkOPbfojU3M1w&s=10'},
|
|
471
|
+
{name:'Spiderman: Homecoming',u:'aHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2ZpbGUvZC8xaFVJeDA1RzNOX3VjZzhTd2RaSFJ0XzVNZHFMUVp0YWkvcHJldmlldw==',poster:'https://resizing.flixster.com/-XZAfHZM39UwaGJIFWKAE8fS0ak=/v3/t/assets/p12798844_p_v8_ao.jpg'},
|
|
472
|
+
{name:'F1',u:'aHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2ZpbGUvZC8xa196UkR0NFJuXzlGb3RZSlVmcmhKWjBaREtmd3FRazEvcHJldmlldw==',poster:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSr2SgmDLkFhLGuF1CnP_xJv1stiKvK027DkN6m6wdqdg&s=10'},
|
|
473
|
+
{name:'Obsession',u:'aHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2ZpbGUvZC8xRkVOMTZMd001WnpUQlNiQnRrWTk1THJJemxzSFhGeDcvcHJldmlldw==',poster:'https://images.contentstack.io/v3/assets/blt13adb7e2033fcee5/blt5c8bcbfee5dade5c/6a2b47a8c284bac540ef3353/Obsession_keyart_poster_2000x3000.jpg?width=2560'}];
|
|
474
|
+
const dec = s => atob(s);
|
|
475
|
+
const AI_KEY = "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";
|
|
476
|
+
|
|
477
|
+
/* ================= SETTINGS ================= */
|
|
478
|
+
const DEFAULTS = {accent:"#ff6b1a", city:"", particles:140, model:"gpt-4o-mini"};
|
|
479
|
+
let S = {...DEFAULTS, ...JSON.parse(localStorage.getItem("igniteos")||"{}")};
|
|
480
|
+
const saveS = () => localStorage.setItem("igniteos", JSON.stringify(S));
|
|
481
|
+
const applyAccent = () => document.documentElement.style.setProperty("--accent", S.accent);
|
|
482
|
+
|
|
483
|
+
/* ================= NAV ================= */
|
|
484
|
+
function go(id){
|
|
485
|
+
document.querySelectorAll(".screen").forEach(s=>s.classList.remove("active"));
|
|
486
|
+
document.getElementById(id).classList.add("active");
|
|
487
|
+
document.querySelectorAll(".dock-btn[data-s]").forEach(b=>b.classList.toggle("on", b.dataset.s===id));
|
|
488
|
+
if(id==="home") startParticles(); else stopParticles();
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/* ================= PARTICLES (falling snow) ================= */
|
|
492
|
+
const cv = document.getElementById("particles"), ctx = cv.getContext("2d");
|
|
493
|
+
let flakes = [], raf = null;
|
|
494
|
+
function sizeCanvas(){ cv.width = innerWidth; cv.height = innerHeight; }
|
|
495
|
+
addEventListener("resize", sizeCanvas); sizeCanvas();
|
|
496
|
+
function makeFlakes(){
|
|
497
|
+
flakes = Array.from({length:S.particles}, () => ({
|
|
498
|
+
x: Math.random()*cv.width, y: Math.random()*cv.height,
|
|
499
|
+
r: Math.random()*2.4+.6, s: Math.random()*.9+.35,
|
|
500
|
+
a: Math.random()*Math.PI*2, w: Math.random()*.6+.15, o: Math.random()*.55+.2
|
|
501
|
+
}));
|
|
502
|
+
}
|
|
503
|
+
function drawFlakes(){
|
|
504
|
+
ctx.clearRect(0,0,cv.width,cv.height);
|
|
505
|
+
const acc = getComputedStyle(document.documentElement).getPropertyValue("--accent").trim() || "#ff6b1a";
|
|
506
|
+
for(const f of flakes){
|
|
507
|
+
f.y += f.s; f.a += .012; f.x += Math.sin(f.a)*f.w;
|
|
508
|
+
if(f.y > cv.height+6){ f.y = -6; f.x = Math.random()*cv.width; }
|
|
509
|
+
const tw = .75 + Math.sin(f.a*2)*.25;
|
|
510
|
+
ctx.beginPath(); ctx.arc(f.x, f.y, f.r, 0, Math.PI*2);
|
|
511
|
+
ctx.fillStyle = f.r > 1.9 ? hexA(acc, f.o*tw*.9) : `rgba(255,255,255,${f.o*tw})`;
|
|
512
|
+
ctx.fill();
|
|
513
|
+
}
|
|
514
|
+
raf = requestAnimationFrame(drawFlakes);
|
|
515
|
+
}
|
|
516
|
+
function hexA(h, a){
|
|
517
|
+
const n = parseInt(h.slice(1),16);
|
|
518
|
+
return `rgba(${n>>16&255},${n>>8&255},${n&255},${a})`;
|
|
519
|
+
}
|
|
520
|
+
function startParticles(){ if(!raf){ makeFlakes(); drawFlakes(); } }
|
|
521
|
+
function stopParticles(){ cancelAnimationFrame(raf); raf = null; ctx.clearRect(0,0,cv.width,cv.height); }
|
|
522
|
+
|
|
523
|
+
/* ================= ROTATING DESCRIPTION ================= */
|
|
524
|
+
const LINES = [
|
|
525
|
+
"21 hand-picked games. Zero loading pain.",
|
|
526
|
+
"A private movie theater, right in your browser.",
|
|
527
|
+
"An AI that codes, explains and builds with you.",
|
|
528
|
+
"Liquid glass. Falling light. Pure black.",
|
|
529
|
+
"Your arcade. Your cinema. Your OS."
|
|
530
|
+
];
|
|
531
|
+
const rot = document.getElementById("rotator");
|
|
532
|
+
LINES.forEach((t,i)=>{
|
|
533
|
+
const d = document.createElement("div");
|
|
534
|
+
d.className = "line" + (i===0?" on":"");
|
|
535
|
+
d.innerHTML = `<span class="tick"></span>${t}`;
|
|
536
|
+
rot.appendChild(d);
|
|
537
|
+
});
|
|
538
|
+
let li = 0;
|
|
539
|
+
setInterval(()=>{
|
|
540
|
+
const lines = rot.children;
|
|
541
|
+
lines[li].classList.replace("on","off");
|
|
542
|
+
const prev = li; li = (li+1)%lines.length;
|
|
543
|
+
setTimeout(()=>lines[prev].classList.remove("off"), 600);
|
|
544
|
+
lines[li].classList.add("on");
|
|
545
|
+
}, 3800);
|
|
546
|
+
|
|
547
|
+
/* ================= CLOCK ================= */
|
|
548
|
+
setInterval(()=>{
|
|
549
|
+
const d = new Date();
|
|
550
|
+
document.getElementById("clockVal").textContent =
|
|
551
|
+
d.toLocaleTimeString([], {hour:"2-digit", minute:"2-digit"});
|
|
552
|
+
}, 1000);
|
|
553
|
+
|
|
554
|
+
/* ================= BATTERY ================= */
|
|
555
|
+
(async ()=>{
|
|
556
|
+
try{
|
|
557
|
+
const b = await navigator.getBattery();
|
|
558
|
+
const upd = ()=>{
|
|
559
|
+
const pct = Math.round(b.level*100);
|
|
560
|
+
document.getElementById("battVal").textContent = pct + "%" + (b.charging?" ⚡":"");
|
|
561
|
+
document.getElementById("battRing").style.strokeDashoffset = 131.9*(1-b.level);
|
|
562
|
+
};
|
|
563
|
+
b.onlevelchange = b.onchargingchange = upd; upd();
|
|
564
|
+
}catch(e){ document.getElementById("battVal").textContent = "N/A"; }
|
|
565
|
+
})();
|
|
1345
566
|
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
567
|
+
/* ================= WEATHER (Open-Meteo, no key needed) ================= */
|
|
568
|
+
const WMO = {0:"☀️",1:"🌤️",2:"⛅",3:"☁️",45:"🌫️",48:"🌫️",51:"🌦️",53:"🌦️",55:"🌧️",56:"🌧️",57:"🌧️",
|
|
569
|
+
61:"🌧️",63:"🌧️",65:"🌧️",66:"🌧️",67:"🌧️",71:"🌨️",73:"🌨️",75:"🌨️",77:"🌨️",80:"🌦️",81:"🌧️",82:"⛈️",
|
|
570
|
+
85:"🌨️",86:"🌨️",95:"⛈️",96:"⛈️",99:"⛈️"};
|
|
571
|
+
async function loadWeather(){
|
|
572
|
+
const val = document.getElementById("wVal"), ic = document.getElementById("wIcon");
|
|
573
|
+
try{
|
|
574
|
+
let lat, lon, name = "";
|
|
575
|
+
if(S.city){
|
|
576
|
+
const g = await fetch(`https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(S.city)}&count=1`).then(r=>r.json());
|
|
577
|
+
if(!g.results?.length) throw 0;
|
|
578
|
+
({latitude:lat, longitude:lon, name} = g.results[0]);
|
|
1353
579
|
} else {
|
|
1354
|
-
|
|
580
|
+
const p = await new Promise((ok,no)=>navigator.geolocation?navigator.geolocation.getCurrentPosition(ok,no,{timeout:5000}):no());
|
|
581
|
+
lat = p.coords.latitude; lon = p.coords.longitude;
|
|
1355
582
|
}
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
$
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
}
|
|
1388
|
-
|
|
1389
|
-
function savePref(patch) {
|
|
1390
|
-
Object.assign(state.prefs, patch);
|
|
1391
|
-
applyPrefs();
|
|
1392
|
-
store.set(K.prefs, state.prefs);
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
function toggleCustomizer(open) {
|
|
1396
|
-
$('customizer').classList.toggle('open', open);
|
|
1397
|
-
if (open) { $('cz-name').value = state.profile.name; $('cz-name').focus(); }
|
|
1398
|
-
beep(open ? 440 : 320, 0.07);
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
|
-
function bindCustomizer() {
|
|
1402
|
-
$('cz-swatches').addEventListener('click', (e) => {
|
|
1403
|
-
const sw = e.target.closest('.swatch');
|
|
1404
|
-
if (!sw) return;
|
|
1405
|
-
const preset = THEME_PRESETS[sw.dataset.preset];
|
|
1406
|
-
if (!preset) return;
|
|
1407
|
-
Object.assign(state.prefs, preset, { preset: sw.dataset.preset });
|
|
1408
|
-
applyPrefs(); store.set(K.prefs, state.prefs);
|
|
1409
|
-
beep(620, 0.09);
|
|
1410
|
-
toast('theme: ' + sw.dataset.preset, 1400);
|
|
1411
|
-
});
|
|
1412
|
-
$('cz-accent').addEventListener('input', (e) => savePref({ accent: e.target.value, preset: null }));
|
|
1413
|
-
$('cz-accent2').addEventListener('input', (e) => savePref({ accent2: e.target.value, preset: null }));
|
|
1414
|
-
$('cz-bgtype').addEventListener('change', (e) => savePref({ bgType: e.target.value, preset: null }));
|
|
1415
|
-
$('cz-bgurl').addEventListener('change', (e) => savePref({ bgUrl: e.target.value.trim() }));
|
|
1416
|
-
$('cz-bg-hue').addEventListener('input', (e) => savePref({ bgHue: Number(e.target.value) }));
|
|
1417
|
-
$('cz-glow').addEventListener('input', (e) => savePref({ glow: Number(e.target.value) }));
|
|
1418
|
-
$('cz-blur').addEventListener('input', (e) => savePref({ blur: Number(e.target.value) }));
|
|
1419
|
-
$('cz-radius').addEventListener('input', (e) => savePref({ radius: Number(e.target.value) }));
|
|
1420
|
-
$('cz-font').addEventListener('change', (e) => savePref({ font: e.target.value }));
|
|
1421
|
-
$('cz-particles').addEventListener('change', (e) => savePref({ particles: e.target.checked }));
|
|
1422
|
-
$('cz-grid').addEventListener('change', (e) => savePref({ grid: e.target.checked }));
|
|
1423
|
-
$('cz-noise').addEventListener('change', (e) => savePref({ noise: e.target.checked }));
|
|
1424
|
-
$('cz-anim').addEventListener('change', (e) => savePref({ anim: e.target.checked }));
|
|
1425
|
-
$('cz-sound').addEventListener('change', (e) => savePref({ sound: e.target.checked }));
|
|
1426
|
-
$('cz-work').addEventListener('change', (e) => setWorkMode(e.target.checked));
|
|
1427
|
-
$('cz-name').addEventListener('change', (e) => {
|
|
1428
|
-
state.profile.name = (e.target.value || 'DRIVER').toUpperCase().slice(0, 20);
|
|
1429
|
-
store.set(K.profile, state.profile);
|
|
1430
|
-
$('profile-pill').textContent = state.profile.name;
|
|
1431
|
-
});
|
|
1432
|
-
$('cz-reset').addEventListener('click', () => {
|
|
1433
|
-
localStorage.removeItem(K.prefs);
|
|
1434
|
-
localStorage.removeItem(K.profile);
|
|
1435
|
-
location.reload();
|
|
1436
|
-
});
|
|
1437
|
-
$('nav-customize').addEventListener('click', () => toggleCustomizer(true));
|
|
1438
|
-
$('cz-close').addEventListener('click', () => toggleCustomizer(false));
|
|
1439
|
-
}
|
|
1440
|
-
// ═══════════ background canvas ═══════════
|
|
1441
|
-
|
|
1442
|
-
const canvas = $('bg-canvas');
|
|
1443
|
-
const ctx = canvas.getContext('2d');
|
|
1444
|
-
let motes = [], starMode = false;
|
|
1445
|
-
function initMotes() {
|
|
1446
|
-
starMode = state.prefs.bgType === 'stars';
|
|
1447
|
-
const count = starMode ? 130 : Math.min(72, Math.floor(canvas.width / 22));
|
|
1448
|
-
motes = Array.from({ length: count }, () => ({
|
|
1449
|
-
x: Math.random() * canvas.width, y: Math.random() * canvas.height,
|
|
1450
|
-
r: starMode ? (Math.random() * 1.4 + 0.4) : (Math.random() * 2 + 0.6),
|
|
1451
|
-
vx: (Math.random() - 0.5) * (starMode ? 0.15 : 0.35),
|
|
1452
|
-
vy: (Math.random() - 0.5) * (starMode ? 0.12 : 0.28),
|
|
1453
|
-
tw: Math.random() * Math.PI * 2, tws: 0.01 + Math.random() * 0.03,
|
|
1454
|
-
hue: Math.random() * 40 - 20,
|
|
1455
|
-
}));
|
|
1456
|
-
}
|
|
1457
|
-
function drawMotes() {
|
|
1458
|
-
if (!state.prefs.particles) { ctx.clearRect(0, 0, canvas.width, canvas.height); return; }
|
|
1459
|
-
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1460
|
-
const rgb = hexToRgb(state.prefs.accent);
|
|
1461
|
-
for (const m of motes) {
|
|
1462
|
-
m.x += m.vx; m.y += m.vy; m.tw += m.tws;
|
|
1463
|
-
if (m.x < -8) m.x = canvas.width + 8; if (m.x > canvas.width + 8) m.x = -8;
|
|
1464
|
-
if (m.y < -8) m.y = canvas.height + 8; if (m.y > canvas.height + 8) m.y = -8;
|
|
1465
|
-
const alpha = starMode ? (0.3 + 0.7 * Math.abs(Math.sin(m.tw))) : (0.1 + 0.42 * Math.abs(Math.sin(m.tw)));
|
|
1466
|
-
ctx.beginPath();
|
|
1467
|
-
ctx.arc(m.x, m.y, m.r, 0, Math.PI * 2);
|
|
1468
|
-
ctx.fillStyle = starMode
|
|
1469
|
-
? 'rgba(255,255,255,' + alpha + ')'
|
|
1470
|
-
: 'hsla(' + (state.prefs.bgHue + m.hue) + ' 90% 65% / ' + alpha + ')';
|
|
1471
|
-
ctx.fill();
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
function bgLoop() {
|
|
1475
|
-
drawMotes();
|
|
1476
|
-
requestAnimationFrame(bgLoop);
|
|
1477
|
-
}
|
|
1478
|
-
window.addEventListener('resize', () => {
|
|
1479
|
-
canvas.width = window.innerWidth; canvas.height = window.innerHeight;
|
|
1480
|
-
initMotes();
|
|
583
|
+
const w = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}¤t_weather=true&temperature_unit=fahrenheit`).then(r=>r.json());
|
|
584
|
+
const cw = w.current_weather;
|
|
585
|
+
val.textContent = Math.round(cw.temperature) + "°F" + (name?` · ${name}`:"");
|
|
586
|
+
ic.textContent = WMO[cw.weathercode] || "⛅";
|
|
587
|
+
}catch(e){ val.textContent = "Set city ⚙"; ic.textContent = "🌍"; }
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/* ================= BUILD GAMES ================= */
|
|
591
|
+
const gamesGrid = document.getElementById("gamesGrid");
|
|
592
|
+
GAMES.forEach((g,i)=>{
|
|
593
|
+
const t = document.createElement("div");
|
|
594
|
+
t.className = "game-tile"; t.style.setProperty("--gc", g.color); t.style.setProperty("--d", (i*0.04)+"s");
|
|
595
|
+
t.innerHTML = `<img src="${g.cover}" alt="" loading="lazy" onerror="this.style.display='none'">
|
|
596
|
+
${g.featured?'<span class="feat-tag">★ FEATURED</span>':''}
|
|
597
|
+
<div class="shine"></div>
|
|
598
|
+
<div class="play-hint"><div class="tri"></div></div>
|
|
599
|
+
<div class="tip">${g.name}</div>`;
|
|
600
|
+
t.onclick = ()=>openPlay(g.name, dec(g.u));
|
|
601
|
+
gamesGrid.appendChild(t);
|
|
602
|
+
});
|
|
603
|
+
document.getElementById("gameCount").textContent = GAMES.length + " titles · click to play";
|
|
604
|
+
|
|
605
|
+
/* library marquee — two counter-scrolling rows, duplicated for seamless loop */
|
|
606
|
+
function buildRail(el, list){
|
|
607
|
+
[...list, ...list].forEach(g=>{
|
|
608
|
+
const d = document.createElement("div");
|
|
609
|
+
d.className = "rail-game"; d.style.setProperty("--gc", g.color);
|
|
610
|
+
d.innerHTML = `<img src="${g.cover}" alt="" loading="lazy" onerror="this.style.display='none'">`;
|
|
611
|
+
d.title = g.name;
|
|
612
|
+
d.onclick = ()=>openPlay(g.name, dec(g.u));
|
|
613
|
+
el.appendChild(d);
|
|
1481
614
|
});
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
store.set(K.last, { name: g.name, ts: Date.now() });
|
|
1570
|
-
$('stat-last').textContent = g.name.slice(0, 12);
|
|
1571
|
-
openLauncher(g.name, url);
|
|
1572
|
-
});
|
|
1573
|
-
$('games-edit-badge').addEventListener('click', () => showGameForm(null));
|
|
1574
|
-
$('launcher-close').addEventListener('click', closeLauncher);
|
|
1575
|
-
$('launcher-back').addEventListener('click', () => { try { $('launcher-frame').contentWindow.history.back(); } catch (e) { /* ignore */ } });
|
|
1576
|
-
$('launcher-refresh').addEventListener('click', () => { $('launcher-frame').src = $('launcher-frame').src; });
|
|
1577
|
-
$('launcher-fs').addEventListener('click', () => {
|
|
1578
|
-
const fr = $('launcher-frame');
|
|
1579
|
-
if (fr.requestFullscreen) fr.requestFullscreen().catch(() => {});
|
|
1580
|
-
});
|
|
1581
|
-
$('launcher-modal').addEventListener('click', (e) => {
|
|
1582
|
-
if (e.target === $('launcher-modal')) closeLauncher();
|
|
1583
|
-
});
|
|
1584
|
-
}
|
|
1585
|
-
|
|
1586
|
-
// ── shared add/edit form (games + movies) ──
|
|
1587
|
-
function showForm(mode, item) {
|
|
1588
|
-
state.formMode = mode;
|
|
1589
|
-
state.formEditId = item ? item.id : null;
|
|
1590
|
-
$('form-title').textContent = (item ? 'Edit ' : ('Add ')) + (mode === 'game' ? 'game' : 'movie');
|
|
1591
|
-
$('form-name').value = item ? item.name || item.title || '' : '';
|
|
1592
|
-
$('form-desc').value = item ? item.desc || '' : '';
|
|
1593
|
-
$('form-link').value = item ? (mode === 'game' ? item.url : (item.url || item.driveId || '')) : '';
|
|
1594
|
-
$('form-genre').value = item ? (mode === 'game' ? (item.tags || []).join(', ') : (item.genre || '')) : '';
|
|
1595
|
-
$('form-poster').value = item ? (item.cover || item.poster || '') : '';
|
|
1596
|
-
$('form-name').placeholder = mode === 'game' ? 'game title' : 'movie title';
|
|
1597
|
-
$('form-link').placeholder = mode === 'game'
|
|
1598
|
-
? 'https://somesite.com/game — proxied automatically'
|
|
1599
|
-
: 'https://drive.google.com/file/d/…/view';
|
|
1600
|
-
$('form-parse-hint').textContent = '';
|
|
1601
|
-
$('form-modal').classList.remove('hidden');
|
|
1602
|
-
setTimeout(() => $('form-name').focus(), 60);
|
|
1603
|
-
}
|
|
1604
|
-
function closeForm() {
|
|
1605
|
-
$('form-modal').classList.add('hidden');
|
|
1606
|
-
state.formEditId = null;
|
|
1607
|
-
}
|
|
1608
|
-
// ═══════════ form handlers ═══════════
|
|
1609
|
-
|
|
1610
|
-
const uid = () => 'm' + Date.now().toString(36) + Math.random().toString(36).slice(2, 7);
|
|
1611
|
-
|
|
1612
|
-
function bindForm() {
|
|
1613
|
-
$('form-cancel').addEventListener('click', closeForm);
|
|
1614
|
-
$('form-modal').addEventListener('click', (e) => { if (e.target === $('form-modal')) closeForm(); });
|
|
1615
|
-
$('form-card').addEventListener('submit', (e) => {
|
|
1616
|
-
e.preventDefault();
|
|
1617
|
-
const mode = state.formMode;
|
|
1618
|
-
const name = $('form-name').value.trim();
|
|
1619
|
-
const desc = $('form-desc').value.trim();
|
|
1620
|
-
const genre = $('form-genre').value.trim();
|
|
1621
|
-
const poster = $('form-poster').value.trim();
|
|
1622
|
-
const link = $('form-link').value.trim();
|
|
1623
|
-
if (!name) { toast('give it a name'); return; }
|
|
1624
|
-
if (mode === 'game') {
|
|
1625
|
-
if (!link) { toast('paste a game URL — it plays through the tunnel'); return; }
|
|
1626
|
-
const id = state.formEditId || uid().replace('m', 'g');
|
|
1627
|
-
const old = state.overrides[id] || {};
|
|
1628
|
-
state.overrides[id] = Object.assign({}, old, {
|
|
1629
|
-
id, name, url: link, desc,
|
|
1630
|
-
tags: genre ? genre.split(',').map((t) => t.trim()).filter(Boolean) : ['custom'],
|
|
1631
|
-
cover: poster || undefined, color: old.color || pickColor(name),
|
|
1632
|
-
});
|
|
1633
|
-
store.set(K.overrides, state.overrides);
|
|
1634
|
-
renderGames();
|
|
1635
|
-
toast('game saved: ' + name); beep(660, 0.09);
|
|
1636
|
-
} else {
|
|
1637
|
-
if (!link) { toast('paste a Google Drive link or video URL'); return; }
|
|
1638
|
-
const parsed = parseDrive(link);
|
|
1639
|
-
const movie = {
|
|
1640
|
-
id: state.formEditId || uid(),
|
|
1641
|
-
title: name, desc, genre: genre || 'uncategorized',
|
|
1642
|
-
poster: poster || (parsed.kind === 'file' ? driveThumb(parsed.id) : ''),
|
|
1643
|
-
source: parsed.kind, driveId: parsed.id || undefined,
|
|
1644
|
-
url: parsed.url || undefined,
|
|
1645
|
-
fav: false, watched: false, progress: 0, added: Date.now(),
|
|
1646
|
-
};
|
|
1647
|
-
const idx = state.movies.findIndex((m) => m.id === movie.id);
|
|
1648
|
-
if (idx > -1) Object.assign(state.movies[idx], movie); else state.movies.unshift(movie);
|
|
1649
|
-
store.set(K.movies, state.movies);
|
|
1650
|
-
renderMovies(); renderGames();
|
|
1651
|
-
toast('movie added: ' + name); beep(660, 0.09);
|
|
1652
|
-
}
|
|
1653
|
-
closeForm();
|
|
1654
|
-
});
|
|
1655
|
-
|
|
1656
|
-
$('form-link').addEventListener('input', () => {
|
|
1657
|
-
const p = parseDrive($('form-link').value);
|
|
1658
|
-
const h = $('form-parse-hint');
|
|
1659
|
-
if (p.kind === 'file') h.textContent = 'Drive file detected ✓ (id ' + p.id.slice(0, 6) + '…)';
|
|
1660
|
-
else if (p.kind === 'folder') h.textContent = 'Drive folder detected ✓ — embeds the folder listing';
|
|
1661
|
-
else if (p.kind === 'url') h.textContent = 'direct video URL — plays in the built-in player';
|
|
1662
|
-
else h.textContent = 'paste a Google Drive share link or a direct video URL';
|
|
1663
|
-
});
|
|
1664
|
-
}
|
|
1665
|
-
|
|
1666
|
-
const pickColor = (name) => {
|
|
1667
|
-
const palette = ['#ff6b1a', '#3b82f6', '#a855f7', '#22d3ee', '#34d399', '#facc15', '#fb7185'];
|
|
1668
|
-
let h = 0; for (let i = 0; i < name.length; i++) h = (h * 31 + name.charCodeAt(i)) | 0;
|
|
1669
|
-
return palette[Math.abs(h) % palette.length];
|
|
1670
|
-
};
|
|
1671
|
-
// ═══════════ movie library ═══════════
|
|
1672
|
-
|
|
1673
|
-
function renderMovies() {
|
|
1674
|
-
const q = state.movieFilter.toLowerCase();
|
|
1675
|
-
let list = state.movies.filter((m) =>
|
|
1676
|
-
(!q || m.title.toLowerCase().includes(q)) &&
|
|
1677
|
-
(!state.movieGenre || (m.genre || '').toLowerCase().includes(state.movieGenre.toLowerCase())));
|
|
1678
|
-
if (state.movieSort === 'title') list = list.slice().sort((a, b) => a.title.localeCompare(b.title));
|
|
1679
|
-
else if (state.movieSort === 'watched') list = list.slice().sort((a, b) => Number(a.watched) - Number(b.watched));
|
|
1680
|
-
else list = list.slice().sort((a, b) => (b.added || 0) - (a.added || 0));
|
|
1681
|
-
|
|
1682
|
-
const genres = ['all'];
|
|
1683
|
-
state.movies.forEach((m) => { const g = (m.genre || '').trim(); if (g && !genres.includes(g)) genres.push(g); });
|
|
1684
|
-
$('genre-chips').innerHTML = genres.map((g) =>
|
|
1685
|
-
'<button class="genre-chip' + ((g === state.movieGenre) || (g === 'all' && !state.movieGenre) ? ' active' : '') +
|
|
1686
|
-
'" data-g="' + esc(g) + '">' + esc(g) + '</button>').join('');
|
|
1687
|
-
|
|
1688
|
-
const wrap = $('movie-grid');
|
|
1689
|
-
wrap.innerHTML = '';
|
|
1690
|
-
list.forEach((m) => wrap.appendChild(movieCard(m)));
|
|
1691
|
-
$('movies-empty').classList.toggle('hidden', state.movies.length > 0);
|
|
1692
|
-
|
|
1693
|
-
const cont = state.movies.filter((m) => m.progress > 7 && m.progress < 95 && !m.watched);
|
|
1694
|
-
$('continue-panel').classList.toggle('hidden', cont.length === 0);
|
|
1695
|
-
if (cont.length) {
|
|
1696
|
-
const cw = $('continue-grid');
|
|
1697
|
-
cw.innerHTML = '';
|
|
1698
|
-
cont.forEach((m) => {
|
|
1699
|
-
const c = movieCard(m);
|
|
1700
|
-
const pct = document.createElement('span');
|
|
1701
|
-
pct.className = 'movie-cont-pct';
|
|
1702
|
-
pct.textContent = Math.round(m.progress) + '%';
|
|
1703
|
-
c.querySelector('.movie-info').appendChild(pct);
|
|
1704
|
-
cw.appendChild(c);
|
|
1705
|
-
});
|
|
1706
|
-
}
|
|
1707
|
-
}
|
|
1708
|
-
|
|
1709
|
-
function movieCard(m) {
|
|
1710
|
-
const card = document.createElement('div');
|
|
1711
|
-
card.className = 'movie-card';
|
|
1712
|
-
card.dataset.id = m.id;
|
|
1713
|
-
card.style.setProperty('--mcolor', m.color || '#ff6b1a');
|
|
1714
|
-
const posterFallback = m.driveId ? 'this.onerror=null;this.src=\'https://drive.google.com/thumbnail?id=' + m.driveId + '&sz=w1000\';' : 'this.remove()';const poster = (m.poster || m.driveId) ? '<img src="' + esc(m.poster || ('https://drive.google.com/thumbnail?id=' + m.driveId + '&sz=w1000')) + '" alt="" loading="lazy" referrerpolicy="no-referrer" onerror="' + posterFallback + '">' : '';
|
|
1715
|
-
const st = [];
|
|
1716
|
-
if (m.fav) st.push('<span class="fav">★</span>');
|
|
1717
|
-
if (m.watched) st.push('<span>✓</span>');
|
|
1718
|
-
else if (m.progress > 7) st.push('<span>' + Math.round(m.progress) + '%</span>');
|
|
1719
|
-
card.innerHTML =
|
|
1720
|
-
'<div class="movie-poster">' +
|
|
1721
|
-
(m.poster ? '' : '<div class="movie-fallback">' + esc((m.title || '?').slice(0, 2).toUpperCase()) + '</div>') + poster +
|
|
1722
|
-
'<div class="movie-play"><i>▶</i></div>' +
|
|
1723
|
-
'<div class="movie-status">' + st.join('') + '</div>' +
|
|
1724
|
-
'<div class="movie-tools">' +
|
|
1725
|
-
'<button class="icon-btn" data-act="fav" title="favorite">★</button>' +
|
|
1726
|
-
'<button class="icon-btn" data-act="watched" title="watched">✓</button>' +
|
|
1727
|
-
'<button class="icon-btn" data-act="edit" title="edit">✎</button>' +
|
|
1728
|
-
'<button class="icon-btn" data-act="del" title="remove">✖</button>' +
|
|
1729
|
-
'</div>' +
|
|
1730
|
-
'</div>' +
|
|
1731
|
-
'<div class="movie-info">' +
|
|
1732
|
-
'<div class="movie-title">' + esc(m.title) + '</div>' +
|
|
1733
|
-
'<div class="movie-genre">' + esc(m.genre || '') + '</div>' +
|
|
1734
|
-
'</div>';
|
|
1735
|
-
return card;
|
|
1736
|
-
}
|
|
1737
|
-
// ═══════════ movie player ═══════════
|
|
1738
|
-
|
|
1739
|
-
function openPlayer(movie) {
|
|
1740
|
-
// normalize any pasted Google Drive link (incl. /preview URLs) into an embedded id
|
|
1741
|
-
if (movie.source === 'url' && movie.url) {
|
|
1742
|
-
const p = parseDrive(movie.url);
|
|
1743
|
-
if (p.kind === 'file') {
|
|
1744
|
-
movie.driveId = p.id;
|
|
1745
|
-
movie.source = 'file';
|
|
1746
|
-
if (!movie.poster) movie.poster = driveThumb(p.id);
|
|
1747
|
-
} else if (p.kind === 'folder') {
|
|
1748
|
-
movie.driveId = p.id;
|
|
1749
|
-
movie.source = 'folder';
|
|
1750
|
-
}
|
|
1751
|
-
}
|
|
1752
|
-
state.playerMovie = movie;
|
|
1753
|
-
// embedded preview by default for Drive files — reliable, in-page, no new tab
|
|
1754
|
-
state.playerEngine = movie.driveId ? 'preview' : 'video';
|
|
1755
|
-
$('player-title').textContent = movie.title;
|
|
1756
|
-
$('player-toggle').classList.toggle('hidden', !movie.driveId);
|
|
1757
|
-
$('player-modal').classList.remove('hidden');
|
|
1758
|
-
document.body.style.overflow = 'hidden';
|
|
1759
|
-
renderPlayerStage();
|
|
1760
|
-
beep(440, 0.08);
|
|
1761
|
-
}
|
|
1762
|
-
function closePlayer() {
|
|
1763
|
-
$('player-stage').innerHTML = '';
|
|
1764
|
-
state.playerMovie = null;
|
|
1765
|
-
$('player-modal').classList.add('hidden');
|
|
1766
|
-
document.body.style.overflow = '';
|
|
1767
|
-
}
|
|
1768
|
-
|
|
1769
|
-
function renderPlayerStage() {
|
|
1770
|
-
const m = state.playerMovie;
|
|
1771
|
-
const stage = $('player-stage');
|
|
1772
|
-
stage.innerHTML = '';
|
|
1773
|
-
if (!m) return;
|
|
1774
|
-
|
|
1775
|
-
// drive folder -> embedded folder listing, in-page
|
|
1776
|
-
if (m.source === 'folder' && m.driveId) {
|
|
1777
|
-
stage.innerHTML = '<iframe src="' + driveFolder(m.driveId) +
|
|
1778
|
-
'" allow="autoplay; fullscreen; encrypted-media; picture-in-picture" allowfullscreen></iframe>';
|
|
1779
|
-
return;
|
|
1780
|
-
}
|
|
1781
|
-
|
|
1782
|
-
// engine 1: direct video stream (raw drive download, or a plain direct URL)
|
|
1783
|
-
if (state.playerEngine === 'video' && (m.driveId || m.url)) {
|
|
1784
|
-
const v = document.createElement('video');
|
|
1785
|
-
v.controls = true; v.autoplay = true; v.playsInline = true;
|
|
1786
|
-
if (m.poster) v.poster = m.poster;
|
|
1787
|
-
v.src = m.driveId ? driveRaw(m.driveId) : m.url;
|
|
1788
|
-
if (m.progress > 7 && m.progress < 95 && m.driveId) {
|
|
1789
|
-
v.addEventListener('loadedmetadata', () => { try { v.currentTime = (m.progress / 100) * v.duration; } catch (e) { /* ignore */ } });
|
|
1790
|
-
}
|
|
1791
|
-
v.addEventListener('timeupdate', () => {
|
|
1792
|
-
if (!v.duration || Number.isNaN(v.duration)) return;
|
|
1793
|
-
const pct = (v.currentTime / v.duration) * 100;
|
|
1794
|
-
const m2 = state.movies.find((x) => x.id === m.id);
|
|
1795
|
-
if (m2 && Math.abs(m2.progress - pct) > 0.5) {
|
|
1796
|
-
m2.progress = Math.min(98, pct);
|
|
1797
|
-
store.set(K.movies, state.movies);
|
|
1798
|
-
}
|
|
1799
|
-
});
|
|
1800
|
-
let settled = false;
|
|
1801
|
-
const fallback = (why) => {
|
|
1802
|
-
if (settled || !m.driveId) return;
|
|
1803
|
-
settled = true;
|
|
1804
|
-
state.playerEngine = 'preview';
|
|
1805
|
-
toast('direct stream unavailable (' + why + ') — embedded player engaged');
|
|
1806
|
-
renderPlayerStage();
|
|
1807
|
-
};
|
|
1808
|
-
v.addEventListener('error', () => fallback('stream blocked'));
|
|
1809
|
-
// some setups stall on an HTML interstitial instead of firing error — force a check
|
|
1810
|
-
setTimeout(() => {
|
|
1811
|
-
if (!settled && v.readyState < 2 && !v.duration) fallback('no stream');
|
|
1812
|
-
}, 7000);
|
|
1813
|
-
stage.appendChild(v);
|
|
1814
|
-
return;
|
|
1815
|
-
}
|
|
1816
|
-
|
|
1817
|
-
// engine 2: embedded Google preview player — plays fully in-page, never a new tab
|
|
1818
|
-
if (m.driveId) {
|
|
1819
|
-
stage.innerHTML = '<iframe src="' + drivePreview(m.driveId) +
|
|
1820
|
-
'" allow="autoplay; fullscreen; encrypted-media; picture-in-picture" allowfullscreen></iframe>';
|
|
1821
|
-
} else if (m.url) {
|
|
1822
|
-
stage.innerHTML = '<iframe src="' + esc(m.url) +
|
|
1823
|
-
'" allow="autoplay; fullscreen" allowfullscreen></iframe>';
|
|
1824
|
-
} else {
|
|
1825
|
-
const box = document.createElement('div');
|
|
1826
|
-
box.className = 'player-error';
|
|
1827
|
-
box.innerHTML = '<h4>Nothing to play</h4><p>This entry has no usable link. Edit it and paste a Google Drive share link.</p>';
|
|
1828
|
-
stage.appendChild(box);
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
|
|
1832
|
-
function bindMovies() {
|
|
1833
|
-
const onGrid = (e) => {
|
|
1834
|
-
const card = e.target.closest('.movie-card');
|
|
1835
|
-
if (!card) return;
|
|
1836
|
-
const m = state.movies.find((x) => x.id === card.dataset.id);
|
|
1837
|
-
if (!m) return;
|
|
1838
|
-
const act = e.target.closest('[data-act]');
|
|
1839
|
-
if (act) {
|
|
1840
|
-
const a = act.dataset.act;
|
|
1841
|
-
if (a === 'fav') { m.fav = !m.fav; store.set(K.movies, state.movies); renderMovies(); toast(m.fav ? '★ favorited' : '★ removed', 1200); }
|
|
1842
|
-
else if (a === 'watched') { m.watched = !m.watched; m.progress = m.watched ? 100 : m.progress; store.set(K.movies, state.movies); renderMovies(); toast(m.watched ? 'marked watched' : 'marked unwatched', 1200); }
|
|
1843
|
-
else if (a === 'edit') { showForm('movie', m); }
|
|
1844
|
-
else if (a === 'del') {
|
|
1845
|
-
if (confirm('Remove "' + m.title + '"?')) {
|
|
1846
|
-
state.movies = state.movies.filter((x) => x.id !== m.id);
|
|
1847
|
-
store.set(K.movies, state.movies);
|
|
1848
|
-
renderMovies(); toast('movie removed');
|
|
1849
|
-
}
|
|
1850
|
-
}
|
|
1851
|
-
return;
|
|
1852
|
-
}
|
|
1853
|
-
openPlayer(m);
|
|
1854
|
-
};
|
|
1855
|
-
$('movie-grid').addEventListener('click', onGrid);
|
|
1856
|
-
$('continue-grid').addEventListener('click', onGrid);
|
|
1857
|
-
|
|
1858
|
-
$('movies-add').addEventListener('click', () => showForm('movie', null));
|
|
1859
|
-
$('movie-search').addEventListener('input', (e) => { state.movieFilter = e.target.value; renderMovies(); });
|
|
1860
|
-
$('genre-chips').addEventListener('click', (e) => {
|
|
1861
|
-
const chip = e.target.closest('.genre-chip');
|
|
1862
|
-
if (!chip) return;
|
|
1863
|
-
state.movieGenre = chip.dataset.g === 'all' ? '' : chip.dataset.g;
|
|
1864
|
-
renderMovies();
|
|
1865
|
-
});
|
|
1866
|
-
$('movie-sort').addEventListener('change', (e) => { state.movieSort = e.target.value; renderMovies(); });
|
|
1867
|
-
$('demo-movies').addEventListener('click', () => {
|
|
1868
|
-
const have = state.movies.map((m) => m.title);
|
|
1869
|
-
DEMO_MOVIES.forEach((dm) => {
|
|
1870
|
-
if (!have.includes(dm.title)) {
|
|
1871
|
-
state.movies.push(Object.assign({}, dm, {
|
|
1872
|
-
id: uid(), url: dm.videoUrl, fav: false, watched: false, progress: 0, added: Date.now(),
|
|
1873
|
-
}));
|
|
1874
|
-
}
|
|
1875
|
-
});
|
|
1876
|
-
store.set(K.movies, state.movies);
|
|
1877
|
-
renderMovies(); renderGames();
|
|
1878
|
-
toast('demo shelf loaded');
|
|
1879
|
-
confetti(40);
|
|
1880
|
-
});
|
|
1881
|
-
|
|
1882
|
-
$('player-close').addEventListener('click', closePlayer);
|
|
1883
|
-
$('player-back').addEventListener('click', closePlayer);
|
|
1884
|
-
$('player-modal').addEventListener('click', (e) => { if (e.target === $('player-modal')) closePlayer(); });
|
|
1885
|
-
$('player-toggle').addEventListener('click', () => {
|
|
1886
|
-
state.playerEngine = state.playerEngine === 'video' ? 'preview' : 'video';
|
|
1887
|
-
renderPlayerStage();
|
|
1888
|
-
toast('player engine: ' + state.playerEngine);
|
|
1889
|
-
});
|
|
1890
|
-
$('player-mark').addEventListener('click', () => {
|
|
1891
|
-
if (!state.playerMovie) return;
|
|
1892
|
-
const m = state.movies.find((x) => x.id === state.playerMovie.id);
|
|
1893
|
-
if (m) { m.watched = true; m.progress = 100; store.set(K.movies, state.movies); renderMovies(); toast('✓ ' + m.title + ' marked watched'); }
|
|
1894
|
-
});
|
|
1895
|
-
}
|
|
1896
|
-
// ═══════════ tunnel ═══════════
|
|
1897
|
-
|
|
1898
|
-
function bindTunnel() { /* tunnel removed — nothing to bind */ }
|
|
1899
|
-
|
|
1900
|
-
// ═══════════ hotkeys & konami ═══════════
|
|
1901
|
-
|
|
1902
|
-
function bindHotkeys() {
|
|
1903
|
-
const konami = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a'];
|
|
1904
|
-
window.addEventListener('keydown', (e) => {
|
|
1905
|
-
const tag = (document.activeElement || {}).tagName;
|
|
1906
|
-
const typing = tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT';
|
|
1907
|
-
if (!typing) {
|
|
1908
|
-
if (e.key === '/') { e.preventDefault(); $('movie-search').focus(); return; }
|
|
1909
|
-
if (e.key === 'g' || e.key === 'G') { $('games').scrollIntoView({ behavior: 'smooth' }); return; }
|
|
1910
|
-
if (e.key === 'm' || e.key === 'M') { $('movies').scrollIntoView({ behavior: 'smooth' }); return; }
|
|
1911
|
-
if (e.key === 'h' || e.key === 'H') { setWorkMode(!state.prefs.work); return; }
|
|
1912
|
-
if (e.key === 'c' || e.key === 'C') { toggleCustomizer(!$('customizer').classList.contains('open')); return; }
|
|
1913
|
-
}
|
|
1914
|
-
if (e.key === 'Escape') {
|
|
1915
|
-
closeLauncher(); closePlayer(); closeForm(); toggleCustomizer(false);
|
|
1916
|
-
}
|
|
1917
|
-
state.keys.push(e.key);
|
|
1918
|
-
if (state.keys.length > konami.length) state.keys.shift();
|
|
1919
|
-
if (state.keys.join(',') === konami.join(',')) {
|
|
1920
|
-
state.keys = [];
|
|
1921
|
-
turboTime();
|
|
1922
|
-
}
|
|
1923
|
-
});
|
|
1924
|
-
}
|
|
1925
|
-
|
|
1926
|
-
function turboTime() {
|
|
1927
|
-
if (state.turbo) return;
|
|
1928
|
-
state.turbo = true;
|
|
1929
|
-
document.body.classList.add('turbo');
|
|
1930
|
-
confetti(90);
|
|
1931
|
-
toast('▲▲▼▼◄►◄►BA — TURBO MODE');
|
|
1932
|
-
beep(880, 0.5, 'square');
|
|
1933
|
-
setTimeout(() => { document.body.classList.remove('turbo'); state.turbo = false; }, 8000);
|
|
1934
|
-
}
|
|
1935
|
-
|
|
1936
|
-
// ═══════════ games form helper ═══════════
|
|
1937
|
-
|
|
1938
|
-
function showGameForm(g) { showForm('game', g); }
|
|
1939
|
-
|
|
1940
|
-
// ═══════════ work mode ═══════════
|
|
1941
|
-
|
|
1942
|
-
const work = {
|
|
1943
|
-
secs: 1500, left: 1500, running: false, timerId: null,
|
|
1944
|
-
todos: store.get('emby.work.v1', []),
|
|
1945
|
-
};
|
|
1946
|
-
const fmtTime = (s) => {
|
|
1947
|
-
s = Math.max(0, Math.round(s));
|
|
1948
|
-
return String(Math.floor(s / 60)).padStart(2, '0') + ':' + String(s % 60).padStart(2, '0');
|
|
1949
|
-
};
|
|
1950
|
-
function renderWorkTimer() { $('focus-timer').textContent = fmtTime(work.left); }
|
|
1951
|
-
function workTick() {
|
|
1952
|
-
if (!work.running) return;
|
|
1953
|
-
work.left -= 1;
|
|
1954
|
-
if (work.left <= 0) {
|
|
1955
|
-
work.left = 0; work.running = false;
|
|
1956
|
-
clearInterval(work.timerId);
|
|
1957
|
-
renderWorkTimer();
|
|
1958
|
-
toast('focus session done');
|
|
1959
|
-
beep(880, 0.6, 'square');
|
|
1960
|
-
confetti(24);
|
|
1961
|
-
return;
|
|
615
|
+
}
|
|
616
|
+
buildRail(document.getElementById("railTrack"), GAMES);
|
|
617
|
+
buildRail(document.getElementById("railTrack2"), [...GAMES].reverse());
|
|
618
|
+
|
|
619
|
+
/* ================= BUILD MOVIES ================= */
|
|
620
|
+
const mg = document.getElementById("moviesGrid");
|
|
621
|
+
MOVIES.forEach((m,i)=>{
|
|
622
|
+
const t = document.createElement("div");
|
|
623
|
+
t.className = "movie-tile"; t.style.setProperty("--d", (i*0.08)+"s");
|
|
624
|
+
t.innerHTML = `<img src="${m.poster}" alt="" loading="lazy"><div class="veil"><div class="tri"></div></div>`;
|
|
625
|
+
t.onclick = ()=>openPlay(m.name, dec(m.u));
|
|
626
|
+
mg.appendChild(t);
|
|
627
|
+
});
|
|
628
|
+
document.getElementById("movieCount").textContent = MOVIES.length + " now streaming";
|
|
629
|
+
|
|
630
|
+
/* ================= PLAY MODAL (no new tabs, no exposed links) ================= */
|
|
631
|
+
const pm = document.getElementById("playModal"), pf = document.getElementById("playFrame"),
|
|
632
|
+
pl = document.getElementById("playLoading");
|
|
633
|
+
function openPlay(title, url){
|
|
634
|
+
document.getElementById("playTitle").textContent = title;
|
|
635
|
+
pl.classList.remove("gone");
|
|
636
|
+
pm.classList.add("open");
|
|
637
|
+
pf.src = url;
|
|
638
|
+
pf.onload = ()=>setTimeout(()=>pl.classList.add("gone"), 400);
|
|
639
|
+
document.body.style.overflow = "hidden";
|
|
640
|
+
}
|
|
641
|
+
function closePlay(){
|
|
642
|
+
pm.classList.remove("open");
|
|
643
|
+
pf.src = "about:blank";
|
|
644
|
+
document.body.style.overflow = "";
|
|
645
|
+
}
|
|
646
|
+
addEventListener("keydown", e=>{ if(e.key==="Escape"){ closePlay(); closeSettings(); } });
|
|
647
|
+
|
|
648
|
+
/* ================= SETTINGS UI ================= */
|
|
649
|
+
const COLORS = ["#ff6b1a","#ff3b30","#ff9500","#ffd60a","#34c759","#00c7be","#32ade6","#7d7aff","#bf5af2","#ff375f"];
|
|
650
|
+
const sw = document.getElementById("swatches");
|
|
651
|
+
COLORS.forEach(c=>{
|
|
652
|
+
const b = document.createElement("div");
|
|
653
|
+
b.className = "swatch" + (S.accent===c?" on":""); b.style.background = c; b.style.color = c;
|
|
654
|
+
b.onclick = ()=>{ S.accent = c; applyAccent(); saveS();
|
|
655
|
+
sw.querySelectorAll(".swatch").forEach(x=>x.classList.remove("on")); b.classList.add("on"); toast("Accent updated"); };
|
|
656
|
+
sw.appendChild(b);
|
|
657
|
+
});
|
|
658
|
+
function openSettings(){
|
|
659
|
+
document.getElementById("cityInput").value = S.city;
|
|
660
|
+
document.getElementById("particleRange").value = S.particles;
|
|
661
|
+
document.getElementById("modelInput").value = S.model;
|
|
662
|
+
document.getElementById("settingsModal").classList.add("open");
|
|
663
|
+
}
|
|
664
|
+
function closeSettings(){ document.getElementById("settingsModal").classList.remove("open"); }
|
|
665
|
+
function saveSettings(){
|
|
666
|
+
S.city = document.getElementById("cityInput").value.trim();
|
|
667
|
+
S.particles = +document.getElementById("particleRange").value;
|
|
668
|
+
S.model = document.getElementById("modelInput").value.trim() || DEFAULTS.model;
|
|
669
|
+
saveS(); makeFlakes(); loadWeather(); closeSettings(); toast("Settings saved");
|
|
670
|
+
}
|
|
671
|
+
function toast(t){
|
|
672
|
+
const el = document.getElementById("toast");
|
|
673
|
+
el.textContent = t; el.classList.add("on");
|
|
674
|
+
clearTimeout(el._t); el._t = setTimeout(()=>el.classList.remove("on"), 2200);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/* ================= AI ================= */
|
|
678
|
+
const chatLog = document.getElementById("chatLog");
|
|
679
|
+
const history = [];
|
|
680
|
+
function addMsg(text, who){
|
|
681
|
+
const d = document.createElement("div");
|
|
682
|
+
d.className = "msg " + who;
|
|
683
|
+
if(who==="bot"){
|
|
684
|
+
d.innerHTML = '<div class="who">IGNITE AI</div>' + renderBot(text);
|
|
685
|
+
} else d.textContent = text;
|
|
686
|
+
chatLog.appendChild(d); chatLog.scrollTop = chatLog.scrollHeight;
|
|
687
|
+
return d;
|
|
688
|
+
}
|
|
689
|
+
function renderBot(text){
|
|
690
|
+
const parts = text.split(/```(\w*)\n?([\s\S]*?)(?:```|$)/g);
|
|
691
|
+
let out = "";
|
|
692
|
+
for(let i=0;i<parts.length;i++){
|
|
693
|
+
if(i%3===0){ out += esc(parts[i]).replace(/\n/g,"<br>"); }
|
|
694
|
+
else if(i%3===2){
|
|
695
|
+
const lang = parts[i-1] || "code";
|
|
696
|
+
out += `<div class="code-widget collapsed">
|
|
697
|
+
<div class="code-head"><div class="dots"><i></i><i></i><i></i></div>
|
|
698
|
+
<span class="lang">${esc(lang)||"code"}</span>
|
|
699
|
+
<button class="collapse">Expand</button>
|
|
700
|
+
<button class="copy">Copy</button></div>
|
|
701
|
+
<pre>${esc(parts[i])}</pre></div>`;
|
|
1962
702
|
}
|
|
1963
|
-
renderWorkTimer();
|
|
1964
703
|
}
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
work.secs = Number(e.target.value); work.left = work.secs; renderWorkTimer();
|
|
704
|
+
return out;
|
|
705
|
+
}
|
|
706
|
+
function esc(s){ return (s||"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); }
|
|
707
|
+
chatLog.addEventListener("click", e=>{
|
|
708
|
+
if(e.target.classList.contains("copy")){
|
|
709
|
+
const w = e.target.closest(".code-widget");
|
|
710
|
+
navigator.clipboard.writeText(w.querySelector("pre").textContent).then(()=>toast("Code copied ✦"));
|
|
711
|
+
}
|
|
712
|
+
if(e.target.classList.contains("collapse")){
|
|
713
|
+
const w = e.target.closest(".code-widget");
|
|
714
|
+
w.classList.toggle("collapsed");
|
|
715
|
+
e.target.textContent = w.classList.contains("collapsed") ? "Expand" : "Collapse";
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
function quickAsk(q){ document.getElementById("aiInput").value = q; sendMsg(); }
|
|
719
|
+
async function sendMsg(){
|
|
720
|
+
const inp = document.getElementById("aiInput"), q = inp.value.trim();
|
|
721
|
+
if(!q) return;
|
|
722
|
+
inp.value = ""; addMsg(q, "user");
|
|
723
|
+
const tp = document.createElement("div");
|
|
724
|
+
tp.className = "msg bot typing"; tp.innerHTML = "<i></i><i></i><i></i>";
|
|
725
|
+
chatLog.appendChild(tp); chatLog.scrollTop = chatLog.scrollHeight;
|
|
726
|
+
try{
|
|
727
|
+
const res = await fetch("https://api.openai.com/v1/chat/completions", {
|
|
728
|
+
method:"POST",
|
|
729
|
+
headers:{ "Content-Type":"application/json", "Authorization":"Bearer " + AI_KEY },
|
|
730
|
+
body: JSON.stringify({
|
|
731
|
+
model: S.model,
|
|
732
|
+
messages: [
|
|
733
|
+
{role:"system", content:"You are IGNITE AI, a super-smart, friendly assistant inside IGNITE OS. You are excellent at coding, explanations, math, creative writing, game ideas and problem solving. Always format code in triple backticks with the language name. Be concise but complete."},
|
|
734
|
+
...history.slice(-10),
|
|
735
|
+
{role:"user", content:q}
|
|
736
|
+
]
|
|
737
|
+
})
|
|
2000
738
|
});
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
});
|
|
2018
|
-
}
|
|
2019
|
-
|
|
2020
|
-
// ═══════════ clocks ═══════════
|
|
2021
|
-
|
|
2022
|
-
function tick() {
|
|
2023
|
-
const d = new Date();
|
|
2024
|
-
const t = d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
2025
|
-
$('nav-clock').textContent = t;
|
|
2026
|
-
$('foot-clock').textContent = new Date().toLocaleDateString([], { weekday: 'short', month: 'short', day: 'numeric' }) + ' · ' + t;
|
|
2027
|
-
}
|
|
2028
|
-
|
|
2029
|
-
// ═══════════ boot splash ═══════════
|
|
2030
|
-
|
|
2031
|
-
const BOOT_LINES = [
|
|
2032
|
-
'boot ignite OS v1.4 …',
|
|
2033
|
-
'game vault … loading',
|
|
2034
|
-
'games vault … ready',
|
|
2035
|
-
'movies (drive) … ready',
|
|
2036
|
-
'weapons hot. have fun, ' + state.profile.name + '.',
|
|
2037
|
-
];
|
|
2038
|
-
|
|
2039
|
-
function runBoot() {
|
|
2040
|
-
const typeEl = $('boot-type');
|
|
2041
|
-
const fill = $('boot-fill');
|
|
2042
|
-
const total = 3000;
|
|
2043
|
-
const start = performance.now();
|
|
2044
|
-
let line = -1;
|
|
2045
|
-
let finished = false;
|
|
2046
|
-
const step = () => {
|
|
2047
|
-
const p = Math.min(1, (performance.now() - start) / total);
|
|
2048
|
-
fill.style.width = Math.round(p * 100) + '%';
|
|
2049
|
-
const targetLine = Math.floor(p * BOOT_LINES.length);
|
|
2050
|
-
if (targetLine > line) {
|
|
2051
|
-
line = targetLine;
|
|
2052
|
-
typeEl.textContent = BOOT_LINES[Math.min(line, BOOT_LINES.length - 1)];
|
|
2053
|
-
}
|
|
2054
|
-
if (p < 1 && state.prefs.anim) requestAnimationFrame(step);
|
|
2055
|
-
else finishBoot();
|
|
2056
|
-
};
|
|
2057
|
-
const finishBoot = () => {
|
|
2058
|
-
if (finished) return;
|
|
2059
|
-
finished = true;
|
|
2060
|
-
fill.style.width = '100%';
|
|
2061
|
-
typeEl.textContent = 'ready. pull up.';
|
|
2062
|
-
setTimeout(() => {
|
|
2063
|
-
const b = $('boot');
|
|
2064
|
-
if (b) { b.classList.add('done'); setTimeout(() => b.remove(), 600); }
|
|
2065
|
-
confetti(26);
|
|
2066
|
-
}, 320);
|
|
2067
|
-
};
|
|
2068
|
-
// failsafe: hard-exit the splash no matter what after 3.5s
|
|
2069
|
-
setTimeout(finishBoot, 3500);
|
|
2070
|
-
if (!state.prefs.anim) { typeEl.textContent = 'ready.'; finishBoot(); return; }
|
|
2071
|
-
requestAnimationFrame(step);
|
|
2072
|
-
$('boot-skip').addEventListener('click', () => { finishBoot(); const b = $('boot'); if (b) setTimeout(() => b.remove(), 300); });
|
|
2073
|
-
}
|
|
2074
|
-
|
|
2075
|
-
// ═══════════ init ═══════════
|
|
2076
|
-
|
|
2077
|
-
async function fetchLocalGames() {
|
|
2078
|
-
try {
|
|
2079
|
-
const res = await fetch(serverBase() + '/lg/');
|
|
2080
|
-
const names = await res.json();
|
|
2081
|
-
state.localGames = names.sort();
|
|
2082
|
-
renderGames();
|
|
2083
|
-
} catch { /* server missing — ignore */ }
|
|
2084
|
-
}
|
|
2085
|
-
|
|
2086
|
-
function seedDemo() {
|
|
2087
|
-
if (state.movies.length) return;
|
|
2088
|
-
state.movies = DEMO_MOVIES.map((dm) => Object.assign({}, dm, {
|
|
2089
|
-
id: uid(), url: dm.videoUrl, fav: false, watched: false, progress: 0, added: Date.now(),
|
|
2090
|
-
}));
|
|
2091
|
-
store.set(K.movies, state.movies);
|
|
2092
|
-
}
|
|
2093
|
-
|
|
2094
|
-
function init() {
|
|
2095
|
-
seedDemo();
|
|
2096
|
-
// repair movies saved by older versions: extract drive ids, fix posters
|
|
2097
|
-
state.movies.forEach((mv) => {
|
|
2098
|
-
if (mv.source === 'url' && mv.url) {
|
|
2099
|
-
const p = parseDrive(mv.url);
|
|
2100
|
-
if (p.kind === 'file') { mv.driveId = p.id; mv.source = 'file'; }
|
|
2101
|
-
else if (p.kind === 'folder') { mv.driveId = p.id; mv.source = 'folder'; }
|
|
2102
|
-
}
|
|
2103
|
-
if (mv.driveId && !mv.poster) mv.poster = driveThumb(mv.driveId);
|
|
2104
|
-
});
|
|
2105
|
-
store.set(K.movies, state.movies);
|
|
2106
|
-
$('profile-pill').textContent = state.profile.name;
|
|
2107
|
-
$('profile-pill').addEventListener('click', () => toggleCustomizer(true));
|
|
2108
|
-
applyPrefs();
|
|
2109
|
-
bindCustomizer();
|
|
2110
|
-
bindGames();
|
|
2111
|
-
bindMovies();
|
|
2112
|
-
bindForm();
|
|
2113
|
-
bindTunnel();
|
|
2114
|
-
bindHotkeys();
|
|
2115
|
-
bindWork();
|
|
2116
|
-
renderWorkTimer();
|
|
2117
|
-
renderTodos();
|
|
2118
|
-
renderGames();
|
|
2119
|
-
renderMovies();
|
|
2120
|
-
tick(); setInterval(tick, 1000);
|
|
2121
|
-
canvas.width = window.innerWidth; canvas.height = window.innerHeight;
|
|
2122
|
-
initMotes();
|
|
2123
|
-
bgLoop();
|
|
2124
|
-
const last = store.get(K.last, {});
|
|
2125
|
-
$('stat-last').textContent = last.name ? last.name.slice(0, 12) : '—';
|
|
2126
|
-
fetchLocalGames();
|
|
2127
|
-
runBoot();
|
|
2128
|
-
}
|
|
2129
|
-
|
|
2130
|
-
// keep it all in-page: block view-source, devtools & right-click menu
|
|
2131
|
-
document.addEventListener('contextmenu', (e) => { e.preventDefault(); });
|
|
2132
|
-
window.addEventListener('contextmenu', (e) => { e.preventDefault(); return false; });
|
|
2133
|
-
document.oncontextmenu = () => false;
|
|
2134
|
-
window.addEventListener('keydown', (e) => {
|
|
2135
|
-
const k = (e.key || '').toLowerCase();
|
|
2136
|
-
const combo = e.ctrlKey || e.metaKey;
|
|
2137
|
-
if (combo && (k === 'u' || k === 's' || k === 'p')) { e.preventDefault(); e.stopPropagation(); return false; }
|
|
2138
|
-
if (combo && e.shiftKey && (k === 'i' || k === 'j' || k === 'c' || k === 'k' || k === 'u')) { e.preventDefault(); e.stopPropagation(); return false; }
|
|
2139
|
-
if (e.key === 'F12') { e.preventDefault(); e.stopPropagation(); return false; }
|
|
2140
|
-
}, true);
|
|
2141
|
-
// if someone opens a view-source: tab, bounce them back to the real page
|
|
2142
|
-
if (location.protocol === 'view-source:') { /* noop guard */ }
|
|
2143
|
-
if (document.documentElement && location.href.startsWith('view-source:')) {
|
|
2144
|
-
location.replace(location.href.replace('view-source:', ''));
|
|
2145
|
-
}
|
|
2146
|
-
|
|
2147
|
-
document.addEventListener('DOMContentLoaded', init);
|
|
2148
|
-
})();
|
|
739
|
+
const data = await res.json();
|
|
740
|
+
tp.remove();
|
|
741
|
+
if(!res.ok || !data.choices) throw new Error(data.error?.message || "API error");
|
|
742
|
+
const reply = data.choices[0].message.content;
|
|
743
|
+
history.push({role:"user",content:q},{role:"assistant",content:reply});
|
|
744
|
+
addMsg(reply, "bot");
|
|
745
|
+
}catch(err){
|
|
746
|
+
tp.remove();
|
|
747
|
+
addMsg("⚠️ Couldn't reach the AI right now. Check the API key / model in Settings, or try again. (" + esc(err.message||"") + ")", "bot");
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
document.getElementById("aiInput").addEventListener("keydown", e=>{ if(e.key==="Enter") sendMsg(); });
|
|
751
|
+
|
|
752
|
+
/* ================= BOOT ================= */
|
|
753
|
+
applyAccent(); startParticles(); loadWeather();
|
|
754
|
+
addMsg("Hey! I'm IGNITE AI ✦ I'm great at a lot of things — coding, explaining, brainstorming, math, you name it. Ask me anything, and if you want code just say so — I'll wrap it in a neat copyable block.", "bot");
|
|
2149
755
|
</script>
|
|
2150
756
|
</body>
|
|
2151
757
|
</html>
|