class-studys 5.0.0 → 7.0.0
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 +130 -11
- package/package.json +1 -1
package/index.html
CHANGED
|
@@ -344,6 +344,22 @@ main { max-width: 1220px; margin: 0 auto; padding: 0 22px 60px; display: grid; g
|
|
|
344
344
|
z-index: 1;
|
|
345
345
|
}
|
|
346
346
|
.game-card:hover .game-cover img { transform: scale(1.06); }
|
|
347
|
+
|
|
348
|
+
/* featured game — wide rectangle, front & center */
|
|
349
|
+
.game-card.featured { grid-column: span 2; }
|
|
350
|
+
.game-card.featured .game-cover { height: 210px; }
|
|
351
|
+
.game-card.featured .game-name { font-size: 1.3rem; }
|
|
352
|
+
.game-card.featured .game-desc { -webkit-line-clamp: 3; font-size: 0.9rem; }
|
|
353
|
+
.game-card.featured .game-tools { top: 42px; }
|
|
354
|
+
.best-badge {
|
|
355
|
+
position: absolute; top: 10px; left: 10px; z-index: 4;
|
|
356
|
+
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
|
357
|
+
color: var(--on-accent); font-size: 0.62rem; font-weight: 900;
|
|
358
|
+
letter-spacing: 1.5px; text-transform: uppercase;
|
|
359
|
+
padding: 4px 12px; border-radius: 99px;
|
|
360
|
+
box-shadow: 0 0 18px color-mix(in srgb, var(--accent) 60%, transparent);
|
|
361
|
+
animation: pulse 2.2s ease-in-out infinite;
|
|
362
|
+
}
|
|
347
363
|
.cover-fallback {
|
|
348
364
|
position: absolute; inset: 0; display: grid; place-items: center;
|
|
349
365
|
font-size: 3rem; font-weight: 900;
|
|
@@ -749,6 +765,7 @@ body[data-anim='off'] *, body[data-anim='off'] *::before, body[data-anim='off']
|
|
|
749
765
|
}
|
|
750
766
|
</style>
|
|
751
767
|
|
|
768
|
+
<base target="_blank">
|
|
752
769
|
<base target="_blank">
|
|
753
770
|
</head>
|
|
754
771
|
<body data-bg="aurora">
|
|
@@ -1040,6 +1057,13 @@ const THEME_PRESETS = {
|
|
|
1040
1057
|
};
|
|
1041
1058
|
|
|
1042
1059
|
const DEFAULT_GAMES = [
|
|
1060
|
+
{
|
|
1061
|
+
id: 'duck-math', name: 'DUCK MATH',
|
|
1062
|
+
url: 'https://unpkg.com/ignite-goober@1.1.4/index.html#/class/drift-king',
|
|
1063
|
+
cover: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRJFXWvY3bIWxbR2SZEnbZaVFRYqFzZJZlOo4SYkWOexg&s',
|
|
1064
|
+
desc: 'the absolute best. quack and calculate.',
|
|
1065
|
+
tags: ['featured','math','racing'], color: '#ffb26b', featured: true,
|
|
1066
|
+
},
|
|
1043
1067
|
{
|
|
1044
1068
|
id: 'Cookie Clicker', name: 'Cookie Clicker TEST',
|
|
1045
1069
|
url: 'https://unpkg.com/edcube-cookie-clicker@5.0.0/index.html',
|
|
@@ -1103,7 +1127,83 @@ const DEFAULT_GAMES = [
|
|
|
1103
1127
|
desc: 'run from cop ',
|
|
1104
1128
|
tags: ['endless','cop'], color: '#fb7185',
|
|
1105
1129
|
},
|
|
1106
|
-
|
|
1130
|
+
{
|
|
1131
|
+
id: 'Bacon May Die', name: 'Bacon May Die',
|
|
1132
|
+
url: 'https://unpkg.com/schooluk-bacon-may-die@1.0.0/index.html',
|
|
1133
|
+
cover: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRG8Ra7wjHyaMeLnHp70ql98oi25b33UsPI63N-Jwbapw&s=10',
|
|
1134
|
+
desc: 'brawler — fight your way through.',
|
|
1135
|
+
tags: ['brawler','action'], color: '#fb7185',
|
|
1136
|
+
},
|
|
1137
|
+
{
|
|
1138
|
+
id: 'paper-fly', name: 'Paper Fly',
|
|
1139
|
+
url: 'https://unpkg.com/papergmfly@1.0.0/index.html',
|
|
1140
|
+
cover: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQQFa7pNRwbmpuk0rwFYmYYjwAdFMI6qH18R1rO_rd8XQ&s=10',
|
|
1141
|
+
desc: 'fold, flick and fly.',
|
|
1142
|
+
tags: ['arcade','2D'], color: '#22d3ee',
|
|
1143
|
+
},
|
|
1144
|
+
{
|
|
1145
|
+
id: 'slope-505', name: 'Slope 505',
|
|
1146
|
+
url: 'https://unpkg.com/sloeplus@1.0.0/index.html',
|
|
1147
|
+
cover: 'https://play-lh.googleusercontent.com/Ev5gkPTONr-1k1r7erT0iiW-pUZoGUH_6FVQdD91e9ghSAqZRMTaFVGQW5Z6PLOfFgxZnJhdEBE2z-TgfZ1bsg',
|
|
1148
|
+
desc: 'the rolling ball, remade.',
|
|
1149
|
+
tags: ['slope','3D'], color: '#fb7185',
|
|
1150
|
+
},
|
|
1151
|
+
{
|
|
1152
|
+
id: 'boat-game', name: 'Boat Game',
|
|
1153
|
+
url: 'https://unpkg.com/boateman@1.0.0/index.html',
|
|
1154
|
+
cover: 'https://play-lh.googleusercontent.com/k80-V4CxUpsoNUTiZCasfAF4yTxy428anUQiM_AieHnLRMq2qmCPIHIYWT8dYATGyo1eTjZLZEa3aa3b4kyingA',
|
|
1155
|
+
desc: 'captain your boat.',
|
|
1156
|
+
tags: ['boat','arcade'], color: '#3b82f6',
|
|
1157
|
+
},
|
|
1158
|
+
{
|
|
1159
|
+
id: '1v1-lol', name: '1v1.lol',
|
|
1160
|
+
url: 'https://unpkg.com/edcube-1v1-lol@5.0.0/index.html',
|
|
1161
|
+
cover: 'https://play-lh.googleusercontent.com/NAE7nd8mxERLlT-eIM68aEQi1RthPUzVd8KN_C-RPmvDgBLJ-C_kBHrBQFYPi8wQwr_nkxGUadoS0xStbU_jc7s=w240-h480-rw',
|
|
1162
|
+
desc: 'build and battle, 1v1.',
|
|
1163
|
+
tags: ['shooter','multiplayer'], color: '#22d3ee',
|
|
1164
|
+
},
|
|
1165
|
+
{
|
|
1166
|
+
id: 'crossy-road', name: 'Crossy Road',
|
|
1167
|
+
url: 'https://unpkg.com/edcube-crossy-road@5.0.0/index.html',
|
|
1168
|
+
cover: 'https://play-lh.googleusercontent.com/LX__Y8tKCvkvEgboZtQfDsvYiNFa6kwWSpqObipZI2MLK5rFVd5tEU8xgMNoMj1ICOf9TO7PFoFmiKGeIpf1=w240-h480-rw',
|
|
1169
|
+
desc: 'cross the road, dodge the traffic.',
|
|
1170
|
+
tags: ['arcade','crossing'], color: '#facc15',
|
|
1171
|
+
},
|
|
1172
|
+
{
|
|
1173
|
+
id: 'basketball-stars', name: 'Basketball Stars',
|
|
1174
|
+
url: 'https://unpkg.com/edcube-basketball-stars@5.0.0/index.html',
|
|
1175
|
+
cover: 'https://m.media-amazon.com/images/I/81BL1Hx2KuL.png',
|
|
1176
|
+
desc: 'sink threes and take the crown.',
|
|
1177
|
+
tags: ['sports','basketball'], color: '#ff6b1a',
|
|
1178
|
+
},
|
|
1179
|
+
{
|
|
1180
|
+
id: 'bitlife', name: 'Bitlife',
|
|
1181
|
+
url: 'https://unpkg.com/edcube-bitlife@5.0.0/index.html',
|
|
1182
|
+
cover: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRZdo5cBqkv1uAcfCofpC-WAViocRNLlysohwHMV2Lqzw&s=10',
|
|
1183
|
+
desc: 'live a whole life, one tap at a time.',
|
|
1184
|
+
tags: ['sim','life'], color: '#34d399',
|
|
1185
|
+
},
|
|
1186
|
+
{
|
|
1187
|
+
id: 'tag', name: 'Tag',
|
|
1188
|
+
url: 'https://unpkg.com/edcube-tag@1.0.0/index.html',
|
|
1189
|
+
cover: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSFwGQ9vG6ZifxDpWHtuu8oSzYqll79xZ9rW_KwPwzCdA&s',
|
|
1190
|
+
desc: "don't get caught.",
|
|
1191
|
+
tags: ['platformer','2D'], color: '#facc15',
|
|
1192
|
+
},
|
|
1193
|
+
{
|
|
1194
|
+
id: 'block-blast', name: 'Block Blast',
|
|
1195
|
+
url: 'https://unpkg.com/edcube-block-blast@5.0.0/index.html',
|
|
1196
|
+
cover: 'https://imgs.crazygames.com/block-puzzle-master_1x1/20250924025352/block-puzzle-master_1x1-cover?format=auto&quality=70&metadata=none',
|
|
1197
|
+
desc: 'blast blocks, clear lines.',
|
|
1198
|
+
tags: ['puzzle'], color: '#a855f7',
|
|
1199
|
+
},
|
|
1200
|
+
{
|
|
1201
|
+
id: 'cut-the-rope', name: 'Cut the Rope',
|
|
1202
|
+
url: 'https://unpkg.com/edcube-cut-the-rope@5.0.0/index.html',
|
|
1203
|
+
cover: 'https://play-lh.googleusercontent.com/2bTmHHM7DBMVe7fLh16Em-J-tVBMFGL2HJCxPAMNF7gAYIfU6EZoYG1__lQ0cvFMEyp3GU9CGijZzEB1qJHF',
|
|
1204
|
+
desc: 'feed the monster, cut the rope.',
|
|
1205
|
+
tags: ['puzzle'], color: '#34d399',
|
|
1206
|
+
},
|
|
1107
1207
|
];
|
|
1108
1208
|
|
|
1109
1209
|
const DEMO_MOVIES = [
|
|
@@ -1132,7 +1232,7 @@ const DEMO_MOVIES = [
|
|
|
1132
1232
|
|
|
1133
1233
|
// ── persistence ──
|
|
1134
1234
|
const K = {
|
|
1135
|
-
games: 'emby.games.v1', movies: 'emby.movies.
|
|
1235
|
+
games: 'emby.games.v1', movies: 'emby.movies.v2',
|
|
1136
1236
|
prefs: 'emby.prefs.v1', profile: 'emby.profile.v1',
|
|
1137
1237
|
last: 'emby.last.v1', overrides: 'emby.overrides.v1',
|
|
1138
1238
|
};
|
|
@@ -1435,7 +1535,7 @@ const DEMO_MOVIES = [
|
|
|
1435
1535
|
wrap.innerHTML = '';
|
|
1436
1536
|
state.games.forEach((g) => {
|
|
1437
1537
|
const card = document.createElement('div');
|
|
1438
|
-
card.className = 'game-card';
|
|
1538
|
+
card.className = 'game-card' + (g.featured ? ' featured' : '');
|
|
1439
1539
|
card.style.setProperty('--gcolor', g.color || '#ff6b1a');
|
|
1440
1540
|
card.dataset.id = g.id;
|
|
1441
1541
|
const cover = g.cover ? '<img src="' + esc(g.cover) + '" alt="" loading="lazy" onerror="this.remove()">' : '';
|
|
@@ -1446,6 +1546,7 @@ const DEMO_MOVIES = [
|
|
|
1446
1546
|
'</div>';
|
|
1447
1547
|
card.innerHTML =
|
|
1448
1548
|
'<div class="game-cover">' +
|
|
1549
|
+
(g.featured ? '<span class="best-badge">★ absolute best</span>' : '') +
|
|
1449
1550
|
(g.cover ? '' : '<div class="cover-fallback">' + esc(g.name.slice(0, 2).toUpperCase()) + '</div>') + cover +
|
|
1450
1551
|
(g.local ? '<span class="local-dot">OFFLINE</span>' : '') + tools +
|
|
1451
1552
|
'<button class="game-play" data-act="play" title="play">▶</button>' +
|
|
@@ -1521,6 +1622,9 @@ const DEMO_MOVIES = [
|
|
|
1521
1622
|
? 'https://somesite.com/game — proxied automatically'
|
|
1522
1623
|
: 'https://drive.google.com/file/d/…/view';
|
|
1523
1624
|
$('form-parse-hint').textContent = '';
|
|
1625
|
+
// editing an existing game: hide the URL field so links stay private
|
|
1626
|
+
// (only the cover image + name/desc/tags can be changed)
|
|
1627
|
+
$('form-link').closest('.form-field').classList.toggle('hidden', mode === 'game' && !!item);
|
|
1524
1628
|
$('form-modal').classList.remove('hidden');
|
|
1525
1629
|
setTimeout(() => $('form-name').focus(), 60);
|
|
1526
1630
|
}
|
|
@@ -1545,11 +1649,11 @@ const DEMO_MOVIES = [
|
|
|
1545
1649
|
const link = $('form-link').value.trim();
|
|
1546
1650
|
if (!name) { toast('give it a name'); return; }
|
|
1547
1651
|
if (mode === 'game') {
|
|
1548
|
-
if (!link) { toast('paste a game URL — it plays through the tunnel'); return; }
|
|
1549
1652
|
const id = state.formEditId || uid().replace('m', 'g');
|
|
1550
1653
|
const old = state.overrides[id] || {};
|
|
1654
|
+
if (!link && !old.url) { toast('paste a game URL — it plays through the tunnel'); return; }
|
|
1551
1655
|
state.overrides[id] = Object.assign({}, old, {
|
|
1552
|
-
id, name, url: link, desc,
|
|
1656
|
+
id, name, url: link || old.url, desc,
|
|
1553
1657
|
tags: genre ? genre.split(',').map((t) => t.trim()).filter(Boolean) : ['custom'],
|
|
1554
1658
|
cover: poster || undefined, color: old.color || pickColor(name),
|
|
1555
1659
|
});
|
|
@@ -1965,6 +2069,7 @@ const DEMO_MOVIES = [
|
|
|
1965
2069
|
const total = 3000;
|
|
1966
2070
|
const start = performance.now();
|
|
1967
2071
|
let line = -1;
|
|
2072
|
+
let finished = false;
|
|
1968
2073
|
const step = () => {
|
|
1969
2074
|
const p = Math.min(1, (performance.now() - start) / total);
|
|
1970
2075
|
fill.style.width = Math.round(p * 100) + '%';
|
|
@@ -1977,16 +2082,21 @@ const DEMO_MOVIES = [
|
|
|
1977
2082
|
else finishBoot();
|
|
1978
2083
|
};
|
|
1979
2084
|
const finishBoot = () => {
|
|
2085
|
+
if (finished) return;
|
|
2086
|
+
finished = true;
|
|
1980
2087
|
fill.style.width = '100%';
|
|
1981
2088
|
typeEl.textContent = 'ready. pull up.';
|
|
1982
2089
|
setTimeout(() => {
|
|
1983
|
-
$('boot')
|
|
1984
|
-
|
|
2090
|
+
const b = $('boot');
|
|
2091
|
+
if (b) { b.classList.add('done'); setTimeout(() => b.remove(), 600); }
|
|
1985
2092
|
confetti(26);
|
|
1986
2093
|
}, 320);
|
|
1987
2094
|
};
|
|
2095
|
+
// failsafe: hard-exit the splash no matter what after 3.5s
|
|
2096
|
+
setTimeout(finishBoot, 3500);
|
|
1988
2097
|
if (!state.prefs.anim) { typeEl.textContent = 'ready.'; finishBoot(); return; }
|
|
1989
2098
|
requestAnimationFrame(step);
|
|
2099
|
+
$('boot-skip').addEventListener('click', () => { finishBoot(); const b = $('boot'); if (b) setTimeout(() => b.remove(), 300); });
|
|
1990
2100
|
}
|
|
1991
2101
|
|
|
1992
2102
|
// ═══════════ init ═══════════
|
|
@@ -2044,13 +2154,22 @@ const DEMO_MOVIES = [
|
|
|
2044
2154
|
runBoot();
|
|
2045
2155
|
}
|
|
2046
2156
|
|
|
2047
|
-
// keep it all in-page: block view-source
|
|
2157
|
+
// keep it all in-page: block view-source, devtools & right-click menu
|
|
2048
2158
|
document.addEventListener('contextmenu', (e) => { e.preventDefault(); });
|
|
2159
|
+
window.addEventListener('contextmenu', (e) => { e.preventDefault(); return false; });
|
|
2160
|
+
document.oncontextmenu = () => false;
|
|
2049
2161
|
window.addEventListener('keydown', (e) => {
|
|
2050
2162
|
const k = (e.key || '').toLowerCase();
|
|
2051
|
-
|
|
2052
|
-
if (
|
|
2053
|
-
|
|
2163
|
+
const combo = e.ctrlKey || e.metaKey;
|
|
2164
|
+
if (combo && (k === 'u' || k === 's' || k === 'p')) { e.preventDefault(); e.stopPropagation(); return false; }
|
|
2165
|
+
if (combo && e.shiftKey && (k === 'i' || k === 'j' || k === 'c' || k === 'k' || k === 'u')) { e.preventDefault(); e.stopPropagation(); return false; }
|
|
2166
|
+
if (e.key === 'F12') { e.preventDefault(); e.stopPropagation(); return false; }
|
|
2167
|
+
}, true);
|
|
2168
|
+
// if someone opens a view-source: tab, bounce them back to the real page
|
|
2169
|
+
if (location.protocol === 'view-source:') { /* noop guard */ }
|
|
2170
|
+
if (document.documentElement && location.href.startsWith('view-source:')) {
|
|
2171
|
+
location.replace(location.href.replace('view-source:', ''));
|
|
2172
|
+
}
|
|
2054
2173
|
|
|
2055
2174
|
document.addEventListener('DOMContentLoaded', init);
|
|
2056
2175
|
})();
|