isaikr 0.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.
Files changed (51) hide show
  1. package/README.md +35 -0
  2. package/cdn/api.js +19 -0
  3. package/cdn/character.js +254 -0
  4. package/cdn/chat.js +33 -0
  5. package/cdn/code-editor.js +1131 -0
  6. package/cdn/community-compose.js +270 -0
  7. package/cdn/games/2048/index.html +12 -0
  8. package/cdn/games/breakout/index.html +13 -0
  9. package/cdn/games/clicker/index.html +26 -0
  10. package/cdn/games/flappy/index.html +11 -0
  11. package/cdn/games/memory/index.html +34 -0
  12. package/cdn/games/pong/index.html +13 -0
  13. package/cdn/games/reaction/index.html +38 -0
  14. package/cdn/games/runner/index.html +11 -0
  15. package/cdn/games/snake/index.html +11 -0
  16. package/cdn/games/tetris/index.html +14 -0
  17. package/cdn/games/whack/index.html +8 -0
  18. package/cdn/go.js +126 -0
  19. package/cdn/go2.js +127 -0
  20. package/cdn/header3_behavior.js +1167 -0
  21. package/cdn/header3_layout.js +1004 -0
  22. package/cdn/header3_layout.js.bak +1004 -0
  23. package/cdn/header3_style.css +3524 -0
  24. package/cdn/header3_style.css.bak +3514 -0
  25. package/cdn/lang.js +198 -0
  26. package/cdn/loading.js +143 -0
  27. package/cdn/loading2.js +144 -0
  28. package/cdn/local-model.js +2941 -0
  29. package/cdn/main.js +4 -0
  30. package/cdn/main_asset.js +1849 -0
  31. package/cdn/main_asset.js.bak +6999 -0
  32. package/cdn/main_index.css +287 -0
  33. package/cdn/re_board3.css +733 -0
  34. package/cdn/re_board3.js +734 -0
  35. package/cdn/re_chat_tts.js +652 -0
  36. package/cdn/re_local_runtime.js +2246 -0
  37. package/cdn/re_local_runtime.js.bak +2246 -0
  38. package/cdn/re_share.js +577 -0
  39. package/cdn/re_voice.js +542 -0
  40. package/cdn/utils.js +36 -0
  41. package/cdn/view.js +321 -0
  42. package/header3_behavior.js +804 -0
  43. package/header3_layout.js +998 -0
  44. package/header3_style.css +2740 -0
  45. package/index.js +0 -0
  46. package/lang.js +179 -0
  47. package/main_asset.js +2416 -0
  48. package/main_index.css +274 -0
  49. package/package.json +14 -0
  50. package/re_chat_tts.js +1419 -0
  51. package/re_voice.js +430 -0
@@ -0,0 +1,270 @@
1
+ // Community post composer: Giphy attachment, image preview, nickname/password panel.
2
+ let selectedCommunityGiphyUrl = "";
3
+ if (typeof window !== "undefined" && typeof window.__ISAI_COMMUNITY_GIPHY_URL__ === "string") {
4
+ selectedCommunityGiphyUrl = window.__ISAI_COMMUNITY_GIPHY_URL__;
5
+ }
6
+
7
+ let giphySearchTimer = null;
8
+ let giphyOffset = 0;
9
+ const giphyPageSize = 24;
10
+ let giphyHasMore = true;
11
+ let giphyLoading = false;
12
+ let giphyCurrentQuery = "";
13
+ const GIPHY_PUBLIC_API_KEY = "JBMEoL3GsHvYoASrdadQhrmDtXTXWEch";
14
+
15
+ function setSelectedCommunityGiphyUrl(url) {
16
+ selectedCommunityGiphyUrl = String(url || "").trim();
17
+ window.__ISAI_COMMUNITY_GIPHY_URL__ = selectedCommunityGiphyUrl;
18
+ const hidden = document.getElementById("comm-image-url");
19
+ if (hidden) hidden.value = selectedCommunityGiphyUrl;
20
+ }
21
+
22
+ function getSelectedCommunityGiphyUrl() {
23
+ const hidden = document.getElementById("comm-image-url");
24
+ if (hidden && String(hidden.value || "").trim()) return String(hidden.value || "").trim();
25
+ if (typeof window.__ISAI_COMMUNITY_GIPHY_URL__ === "string") return String(window.__ISAI_COMMUNITY_GIPHY_URL__ || "").trim();
26
+ return String(selectedCommunityGiphyUrl || "").trim();
27
+ }
28
+
29
+ function getCommunityPreviewNodes() {
30
+ return {
31
+ box: document.getElementById("comm-img-preview-box"),
32
+ img: document.getElementById("comm-preview-img"),
33
+ file: document.getElementById("comm-file-input"),
34
+ attachmentRow: document.getElementById("prompt-attachment-row")
35
+ };
36
+ }
37
+
38
+ function renderCommunityAttachmentRow(url) {
39
+ const row = getCommunityPreviewNodes().attachmentRow;
40
+ if (!row) return;
41
+ const src = String(url || "").trim();
42
+ if (!src) {
43
+ row.innerHTML = "";
44
+ row.classList.add("hidden");
45
+ return;
46
+ }
47
+ const safeSrc = src.replace(/"/g, """);
48
+ row.innerHTML = `<div class="inline-flex items-center gap-2 px-2 py-1 rounded-xl bg-white/8 border border-white/10 mt-1 mb-1">
49
+ <img src="${safeSrc}" class="h-10 w-10 rounded-lg object-cover border border-white/20" alt="attachment">
50
+ <span class="text-[11px] text-white/70">Image attached</span>
51
+ <button type="button" onclick="clearCommFile()" class="h-6 w-6 rounded-full bg-red-500/90 text-white text-[12px] leading-none flex items-center justify-center" title="Remove">&times;</button>
52
+ </div>`;
53
+ row.classList.remove("hidden");
54
+ }
55
+
56
+ function setCommunityImagePreview(url) {
57
+ const nodes = getCommunityPreviewNodes();
58
+ const src = String(url || "").trim();
59
+ if (!src) {
60
+ if (nodes.img) nodes.img.src = "";
61
+ if (nodes.box) nodes.box.classList.add("hidden");
62
+ renderCommunityAttachmentRow("");
63
+ return;
64
+ }
65
+ if (nodes.img) nodes.img.src = src;
66
+ if (nodes.box) nodes.box.classList.remove("hidden");
67
+ renderCommunityAttachmentRow(src);
68
+ }
69
+
70
+ function handleCommFileSelect(input) {
71
+ setSelectedCommunityGiphyUrl("");
72
+ const file = input && input.files && input.files[0] ? input.files[0] : null;
73
+ setCommunityImagePreview(file ? URL.createObjectURL(file) : "");
74
+ }
75
+
76
+ function clearCommFile() {
77
+ setSelectedCommunityGiphyUrl("");
78
+ const nodes = getCommunityPreviewNodes();
79
+ if (nodes.file) nodes.file.value = "";
80
+ setCommunityImagePreview("");
81
+ }
82
+
83
+ function selectGiphyImage(url) {
84
+ const value = String(url || "").trim();
85
+ if (!value) return;
86
+ setSelectedCommunityGiphyUrl(value);
87
+ const nodes = getCommunityPreviewNodes();
88
+ if (nodes.file) nodes.file.value = "";
89
+ setCommunityImagePreview(value);
90
+ if (typeof showToast === "function") showToast("Image selected");
91
+ closeGiphyPicker();
92
+ }
93
+
94
+ function setGiphyPanelOpen(open) {
95
+ const panel = document.getElementById("giphy-inline-panel");
96
+ if (!panel) return;
97
+ panel.classList.toggle("pointer-events-none", !open);
98
+ panel.style.opacity = open ? "1" : "0";
99
+ panel.style.transform = open ? "translateY(0)" : "translateY(-6px)";
100
+ panel.style.maxHeight = open ? "180px" : "0";
101
+ }
102
+
103
+ function openGiphyPicker() {
104
+ const input = document.getElementById("giphy-search-input");
105
+ const results = document.getElementById("giphy-results");
106
+ setGiphyPanelOpen(true);
107
+ setTimeout(() => {
108
+ if (input) input.focus();
109
+ }, 30);
110
+ if (results && !results.dataset.loaded && !giphyLoading) searchGiphyImages("trending");
111
+ }
112
+
113
+ function closeGiphyPicker() {
114
+ setGiphyPanelOpen(false);
115
+ }
116
+
117
+ function toggleGiphyInlineSearch() {
118
+ const panel = document.getElementById("giphy-inline-panel");
119
+ const isOpen = panel && panel.style.opacity === "1";
120
+ if (isOpen) closeGiphyPicker();
121
+ else openGiphyPicker();
122
+ }
123
+
124
+ function queueGiphySearch() {
125
+ clearTimeout(giphySearchTimer);
126
+ giphySearchTimer = setTimeout(() => searchGiphyImages(), 260);
127
+ }
128
+
129
+ function getGiphyImageUrl(item) {
130
+ if (!item || !item.images) return "";
131
+ return (
132
+ item.images.fixed_height && item.images.fixed_height.url ||
133
+ item.images.fixed_width && item.images.fixed_width.url ||
134
+ item.images.original && item.images.original.url ||
135
+ ""
136
+ );
137
+ }
138
+
139
+ function renderGiphyResults(items, append = false) {
140
+ const results = document.getElementById("giphy-results");
141
+ if (!results) return;
142
+
143
+ const pick = (event) => {
144
+ const target = event.target && event.target.closest ? event.target.closest("[data-giphy-url]") : null;
145
+ if (!target) return;
146
+ event.preventDefault();
147
+ event.stopPropagation();
148
+ selectGiphyImage(target.getAttribute("data-giphy-url") || "");
149
+ };
150
+
151
+ if (!results.dataset.bindSelect) {
152
+ results.dataset.bindSelect = "1";
153
+ results.addEventListener("click", pick);
154
+ results.addEventListener("mousedown", pick);
155
+ results.addEventListener("touchstart", pick, { passive: false });
156
+ }
157
+
158
+ if (!results.dataset.bindScroll) {
159
+ results.dataset.bindScroll = "1";
160
+ results.addEventListener("scroll", () => {
161
+ if (giphyLoading || !giphyHasMore) return;
162
+ if (results.scrollHeight - results.scrollTop - results.clientHeight < 64) searchGiphyImages("append");
163
+ });
164
+ }
165
+
166
+ results.dataset.loaded = "1";
167
+ if (!Array.isArray(items) || !items.length) {
168
+ if (!append) results.innerHTML = '<div class="col-span-full text-center text-white/45 text-xs py-8">No images found.</div>';
169
+ return;
170
+ }
171
+
172
+ if (!append) results.innerHTML = "";
173
+ items.forEach((item) => {
174
+ const url = getGiphyImageUrl(item);
175
+ if (!url) return;
176
+ const thumb = (item.images && item.images.fixed_width_small && item.images.fixed_width_small.url) || url;
177
+ const button = document.createElement("button");
178
+ button.type = "button";
179
+ button.dataset.giphyUrl = url;
180
+ button.className = "group relative aspect-square overflow-hidden rounded-xl bg-white/5 border border-white/10 hover:border-white/35 transition";
181
+ const img = document.createElement("img");
182
+ img.src = thumb;
183
+ img.alt = "";
184
+ img.loading = "lazy";
185
+ img.className = "w-full h-full object-cover";
186
+ const badge = document.createElement("span");
187
+ badge.className = "absolute inset-x-2 bottom-2 h-6 rounded-full bg-black/70 text-white text-[11px] font-bold hidden group-hover:flex items-center justify-center";
188
+ badge.textContent = "Select";
189
+ button.appendChild(img);
190
+ button.appendChild(badge);
191
+ results.appendChild(button);
192
+ });
193
+ }
194
+
195
+ async function fetchGiphyImages(query, limit, offset) {
196
+ const localEndpoint = `re_store.php?action=giphy_search&q=${encodeURIComponent(query)}&limit=${limit}&offset=${offset}`;
197
+ try {
198
+ const response = await fetch(localEndpoint, { cache: "no-store" });
199
+ const data = await response.json();
200
+ if (data && data.success) return data;
201
+ } catch (error) {}
202
+
203
+ const apiBase = query
204
+ ? `https://api.giphy.com/v1/gifs/search?api_key=${encodeURIComponent(GIPHY_PUBLIC_API_KEY)}&q=${encodeURIComponent(query)}`
205
+ : `https://api.giphy.com/v1/gifs/trending?api_key=${encodeURIComponent(GIPHY_PUBLIC_API_KEY)}`;
206
+ const response = await fetch(`${apiBase}&limit=${limit}&offset=${offset}&rating=pg-13&lang=ko`, { cache: "no-store" });
207
+ const data = await response.json();
208
+ return {
209
+ success: !!(data && Array.isArray(data.data)),
210
+ data: data && Array.isArray(data.data) ? data.data : []
211
+ };
212
+ }
213
+
214
+ async function searchGiphyImages(mode = "") {
215
+ const input = document.getElementById("giphy-search-input");
216
+ const results = document.getElementById("giphy-results");
217
+ const append = mode === "append";
218
+ const query = String(mode === "trending" ? "" : (input && input.value ? input.value : "")).trim();
219
+
220
+ if (!append) {
221
+ giphyOffset = 0;
222
+ giphyHasMore = true;
223
+ giphyCurrentQuery = query;
224
+ if (results) results.innerHTML = '<div class="col-span-full text-center text-white/45 text-xs py-8">Loading...</div>';
225
+ }
226
+ if (giphyLoading || (append && !giphyHasMore)) return;
227
+
228
+ giphyLoading = true;
229
+ try {
230
+ const data = await fetchGiphyImages(giphyCurrentQuery, giphyPageSize, giphyOffset);
231
+ const items = data && data.success ? (data.data || []) : [];
232
+ renderGiphyResults(items, append);
233
+ giphyOffset += items.length;
234
+ giphyHasMore = items.length >= giphyPageSize;
235
+ } catch (error) {
236
+ if (!append && results) results.innerHTML = '<div class="col-span-full text-center text-red-300 text-xs py-8">Giphy loading failed.</div>';
237
+ } finally {
238
+ giphyLoading = false;
239
+ if (results && giphyHasMore && results.dataset.loaded && results.scrollHeight <= results.clientHeight + 8) {
240
+ setTimeout(() => searchGiphyImages("append"), 30);
241
+ }
242
+ }
243
+ }
244
+
245
+ function toggleCommunityIdentityPanel(force) {
246
+ const panel = document.getElementById("community-identity-panel");
247
+ if (!panel) return;
248
+ const open = typeof force === "boolean" ? force : panel.style.opacity !== "1";
249
+ panel.classList.toggle("pointer-events-none", !open);
250
+ panel.style.opacity = open ? "1" : "0";
251
+ panel.style.transform = open ? "translateX(0)" : "translateX(-8px)";
252
+ panel.style.maxWidth = open ? "240px" : "0";
253
+ panel.style.paddingLeft = open ? "8px" : "0";
254
+ panel.style.paddingRight = open ? "8px" : "0";
255
+ if (open) {
256
+ const nick = document.getElementById("comm-nickname");
257
+ setTimeout(() => nick && nick.focus(), 120);
258
+ }
259
+ }
260
+
261
+ window.getSelectedCommunityGiphyUrl = getSelectedCommunityGiphyUrl;
262
+ window.handleCommFileSelect = handleCommFileSelect;
263
+ window.clearCommFile = clearCommFile;
264
+ window.openGiphyPicker = openGiphyPicker;
265
+ window.closeGiphyPicker = closeGiphyPicker;
266
+ window.toggleGiphyInlineSearch = toggleGiphyInlineSearch;
267
+ window.queueGiphySearch = queueGiphySearch;
268
+ window.searchGiphyImages = searchGiphyImages;
269
+ window.selectGiphyImage = selectGiphyImage;
270
+ window.toggleCommunityIdentityPanel = toggleCommunityIdentityPanel;
@@ -0,0 +1,12 @@
1
+ <!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>2048</title>
2
+ <style>body{margin:0;display:grid;place-items:center;height:100vh;background:#111827;color:#fff;font-family:system-ui}.g{display:grid;grid-template:repeat(4,70px)/repeat(4,70px);gap:8px}.c{display:grid;place-items:center;background:#1f2937;border-radius:10px;font-weight:800}</style>
3
+ <h1>2048</h1><div class=g id=g></div><script>
4
+ let b=[...Array(4)].map(()=>Array(4).fill(0));function r(){const e=[];for(let y=0;y<4;y++)for(let x=0;x<4;x++)if(!b[y][x])e.push([y,x]);if(!e.length)return;const [y,x]=e[Math.random()*e.length|0];b[y][x]=Math.random()<.9?2:4}
5
+ function draw(){g.innerHTML='';for(let y=0;y<4;y++)for(let x=0;x<4;x++){const d=document.createElement('div');d.className='c';d.textContent=b[y][x]||'';g.appendChild(d)}}
6
+ function move(dir){let moved=0;const tr=a=>{a=a.filter(v=>v);for(let i=0;i<a.length-1;i++)if(a[i]===a[i+1]){a[i]*=2;a[i+1]=0}return a.filter(v=>v).concat(Array(4).fill(0)).slice(0,4)};
7
+ for(let i=0;i<4;i++){let l=[];for(let j=0;j<4;j++)l.push(dir%2?b[j][i]:b[i][j]);if(dir>1)l=l.reverse();let n=tr(l);if(dir>1)n=n.reverse();for(let j=0;j<4;j++){if(dir%2){if(b[j][i]!==n[j])moved=1;b[j][i]=n[j]}else{if(b[i][j]!==n[j])moved=1;b[i][j]=n[j]}}}
8
+ if(moved){r();draw()}}
9
+ onkeydown=e=>{if(e.key==='ArrowLeft')move(0);if(e.key==='ArrowUp')move(1);if(e.key==='ArrowRight')move(2);if(e.key==='ArrowDown')move(3);}
10
+ r();r();draw();
11
+ </script>
12
+
@@ -0,0 +1,13 @@
1
+ <!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Breakout</title>
2
+ <style>body{margin:0;display:grid;place-items:center;height:100vh;background:#0a0f1d}canvas{background:#111;border:1px solid #333}</style>
3
+ <canvas id=c width=420 height=300></canvas><script>
4
+ const c=document.getElementById('c'),x=c.getContext('2d');let px=180,bx=210,by=150,vx=3,vy=-3;
5
+ const bricks=[...Array(5)].map((_,r)=>[...Array(10)].map((_,k)=>({x:k*40+10,y:r*16+16,on:1})));onmousemove=e=>{const r=c.getBoundingClientRect();px=Math.max(0,Math.min(340,e.clientX-r.left-40));};
6
+ function d(){x.clearRect(0,0,420,300);x.fillStyle='#fff';x.fillRect(px,280,80,10);x.beginPath();x.arc(bx,by,6,0,7);x.fill();
7
+ x.fillStyle='#3b82f6';bricks.forEach(row=>row.forEach(b=>b.on&&x.fillRect(b.x,b.y,36,12)));
8
+ bx+=vx;by+=vy;if(bx<6||bx>414)vx*=-1;if(by<6)vy*=-1;if(by>300){bx=210;by=150;}
9
+ if(by>272&&bx>px&&bx<px+80)vy=-Math.abs(vy);
10
+ bricks.forEach(row=>row.forEach(b=>{if(b.on&&bx>b.x&&bx<b.x+36&&by>b.y&&by<b.y+12){b.on=0;vy*=-1;}}));
11
+ requestAnimationFrame(d)}d();
12
+ </script>
13
+
@@ -0,0 +1,26 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Clicker</title>
7
+ <style>
8
+ body{margin:0;display:grid;place-items:center;min-height:100vh;background:#0b1020;color:#fff;font-family:system-ui}
9
+ .card{width:min(92vw,420px);background:#111a33;border:1px solid #2b3a68;border-radius:20px;padding:24px;text-align:center}
10
+ button{width:160px;height:160px;border-radius:999px;border:none;background:#3b82f6;color:#fff;font-size:20px;font-weight:700;cursor:pointer}
11
+ .score{font-size:38px;font-weight:800;margin:8px 0 18px}
12
+ </style>
13
+ </head>
14
+ <body>
15
+ <div class="card">
16
+ <h1>Clicker Game</h1>
17
+ <div class="score" id="score">0</div>
18
+ <button id="btn">CLICK</button>
19
+ </div>
20
+ <script>
21
+ let s=0;const score=document.getElementById('score');
22
+ document.getElementById('btn').onclick=()=>{s++;score.textContent=s;};
23
+ </script>
24
+ </body>
25
+ </html>
26
+
@@ -0,0 +1,11 @@
1
+ <!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Flappy</title>
2
+ <style>body{margin:0;display:grid;place-items:center;height:100vh;background:#0ea5e9}canvas{background:#7dd3fc;border:1px solid #0369a1}</style>
3
+ <canvas id=c width=320 height=480></canvas><script>
4
+ const c=document.getElementById('c'),x=c.getContext('2d');let y=220,v=0,g=.35,score=0,pipes=[{x:360,gap:220},{x:540,gap:180}];
5
+ onkeydown=e=>{if(e.code==='Space')v=-6};onclick=()=>v=-6;
6
+ function reset(){y=220;v=0;score=0;pipes=[{x:360,gap:220},{x:540,gap:180}]}
7
+ function d(){v+=g;y+=v;x.fillStyle='#7dd3fc';x.fillRect(0,0,320,480);x.fillStyle='#16a34a';
8
+ pipes.forEach(p=>{p.x-=2;if(p.x<-40){p.x=360;p.gap=120+Math.random()*220;score++}x.fillRect(p.x,0,40,p.gap-70);x.fillRect(p.x,p.gap+70,40,480);if(160>p.x&&160<p.x+40&&(y<p.gap-70||y>p.gap+70))reset();});
9
+ x.fillStyle='#f59e0b';x.beginPath();x.arc(160,y,12,0,7);x.fill();if(y<0||y>480)reset();x.fillStyle='#111';x.fillText('Score '+score,8,16);requestAnimationFrame(d)}d();
10
+ </script>
11
+
@@ -0,0 +1,34 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Memory Flip</title>
7
+ <style>
8
+ body{margin:0;min-height:100vh;display:grid;place-items:center;background:#0f172a;color:#fff;font-family:system-ui}
9
+ .wrap{width:min(92vw,460px)}
10
+ .grid{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}
11
+ .c{height:74px;border-radius:10px;background:#1e293b;display:grid;place-items:center;cursor:pointer;font-size:28px}
12
+ </style>
13
+ </head>
14
+ <body>
15
+ <div class="wrap">
16
+ <h1>Memory Flip</h1>
17
+ <div class="grid" id="g"></div>
18
+ </div>
19
+ <script>
20
+ const icons=['🍎','🍌','🍇','🍒','🍎','🍌','🍇','🍒','🐶','🐱','🐸','🦊','🐶','🐱','🐸','🦊']
21
+ .sort(()=>Math.random()-0.5);
22
+ const g=document.getElementById('g'); let first=null, lock=false;
23
+ icons.forEach((v,i)=>{const d=document.createElement('button');d.className='c';d.textContent='?';d.dataset.v=v;d.onclick=()=>flip(d);g.appendChild(d);});
24
+ function flip(el){
25
+ if(lock||el.disabled||el===first) return;
26
+ el.textContent=el.dataset.v;
27
+ if(!first){first=el;return;}
28
+ if(first.dataset.v===el.dataset.v){first.disabled=el.disabled=true;first=null;return;}
29
+ lock=true;setTimeout(()=>{first.textContent='?';el.textContent='?';first=null;lock=false;},600);
30
+ }
31
+ </script>
32
+ </body>
33
+ </html>
34
+
@@ -0,0 +1,13 @@
1
+ <!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Pong</title>
2
+ <style>body{margin:0;display:grid;place-items:center;height:100vh;background:#0f172a}canvas{background:#111;border:1px solid #333}</style>
3
+ <canvas id=c width=480 height=280></canvas><script>
4
+ const c=document.getElementById('c'),x=c.getContext('2d');let ly=110,ry=110,bx=240,by=140,vx=3,vy=2,ls=0,rs=0;
5
+ onmousemove=e=>{const r=c.getBoundingClientRect();ly=Math.max(0,Math.min(220,e.clientY-r.top-30));};
6
+ function reset(){bx=240;by=140;vx*=-1;}
7
+ function loop(){ry+=((by-30)-ry)*.08;bx+=vx;by+=vy;if(by<6||by>274)vy*=-1;
8
+ if(bx<18&&by>ly&&by<ly+60)vx=Math.abs(vx);if(bx>462&&by>ry&&by<ry+60)vx=-Math.abs(vx);
9
+ if(bx<0){rs++;reset()}if(bx>480){ls++;reset()}
10
+ x.clearRect(0,0,480,280);x.fillStyle='#fff';x.fillRect(8,ly,10,60);x.fillRect(462,ry,10,60);x.fillRect(bx-5,by-5,10,10);x.fillText(ls+' : '+rs,225,16);
11
+ requestAnimationFrame(loop)}loop();
12
+ </script>
13
+
@@ -0,0 +1,38 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Reaction Test</title>
7
+ <style>
8
+ body{margin:0;min-height:100vh;display:grid;place-items:center;background:#101010;color:#fff;font-family:system-ui}
9
+ .box{width:min(92vw,440px);padding:22px;border-radius:18px;background:#1a1a1a;border:1px solid #333;text-align:center}
10
+ .pad{height:180px;border-radius:14px;display:grid;place-items:center;font-weight:700;font-size:22px;background:#7f1d1d}
11
+ button{margin-top:14px;height:42px;padding:0 16px;border-radius:10px;border:none;background:#f59e0b;color:#111;font-weight:700;cursor:pointer}
12
+ </style>
13
+ </head>
14
+ <body>
15
+ <div class="box">
16
+ <h1>Reaction Test</h1>
17
+ <p id="hint">Press Start, then click when green.</p>
18
+ <div id="pad" class="pad">WAIT</div>
19
+ <button id="start">START</button>
20
+ </div>
21
+ <script>
22
+ let started=0, ready=0, timer=0;
23
+ const pad=document.getElementById('pad'),hint=document.getElementById('hint');
24
+ document.getElementById('start').onclick=()=>{
25
+ ready=0;pad.style.background='#7f1d1d';pad.textContent='WAIT';
26
+ hint.textContent='Get ready...';
27
+ const t=1000+Math.random()*3000;
28
+ clearTimeout(timer); timer=setTimeout(()=>{ready=1;started=performance.now();pad.style.background='#166534';pad.textContent='CLICK!';},t);
29
+ };
30
+ pad.onclick=()=>{
31
+ if(!ready){hint.textContent='Too early! Try again.';return;}
32
+ const ms=Math.round(performance.now()-started);
33
+ hint.textContent=`${ms} ms`; ready=0; pad.textContent='DONE'; pad.style.background='#1e3a8a';
34
+ };
35
+ </script>
36
+ </body>
37
+ </html>
38
+
@@ -0,0 +1,11 @@
1
+ <!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Runner</title>
2
+ <style>body{margin:0;display:grid;place-items:center;height:100vh;background:#0b0f1a}canvas{background:#111;border:1px solid #333}</style>
3
+ <canvas id=c width=460 height=220></canvas><script>
4
+ const c=document.getElementById('c'),x=c.getContext('2d');let py=170,v=0,g=.6,ob=460,sc=0,j=0;
5
+ onkeydown=e=>{if((e.code==='Space'||e.key==='ArrowUp')&&!j){v=-10;j=1}};onclick=()=>{if(!j){v=-10;j=1}};
6
+ function d(){v+=g;py+=v;if(py>=170){py=170;v=0;j=0}ob-=5;if(ob<-20){ob=460;sc++}
7
+ if(ob<70&&ob>40&&py>150){sc=0;ob=460}
8
+ x.fillStyle='#111';x.fillRect(0,0,460,220);x.fillStyle='#444';x.fillRect(0,190,460,2);x.fillStyle='#3b82f6';x.fillRect(50,py,20,20);x.fillStyle='#ef4444';x.fillRect(ob,160,14,30);x.fillStyle='#fff';x.fillText('Score '+sc,8,16);
9
+ requestAnimationFrame(d)}d();
10
+ </script>
11
+
@@ -0,0 +1,11 @@
1
+ <!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
2
+ <title>Snake</title><style>body{margin:0;display:grid;place-items:center;height:100vh;background:#0b0f1a;color:#fff;font-family:system-ui}canvas{background:#111;border:1px solid #333}</style>
3
+ <canvas id=c width=360 height=360></canvas><script>
4
+ const c=document.getElementById('c'),x=c.getContext('2d'),s=20;let d=[1,0],p=[[5,5]],f=[10,10],sp=120,sc=0;
5
+ onkeydown=e=>{if(e.key==='ArrowUp'&&d[1]!==1)d=[0,-1];if(e.key==='ArrowDown'&&d[1]!==-1)d=[0,1];if(e.key==='ArrowLeft'&&d[0]!==1)d=[-1,0];if(e.key==='ArrowRight'&&d[0]!==-1)d=[1,0];};
6
+ function tick(){let h=[p[0][0]+d[0],p[0][1]+d[1]];if(h[0]<0||h[1]<0||h[0]>=18||h[1]>=18||p.some(v=>v[0]===h[0]&&v[1]===h[1])){p=[[5,5]];d=[1,0];sc=0;}
7
+ p.unshift(h);if(h[0]===f[0]&&h[1]===f[1]){sc++;f=[Math.floor(Math.random()*18),Math.floor(Math.random()*18)]}else p.pop();
8
+ x.fillStyle='#111';x.fillRect(0,0,360,360);x.fillStyle='#22c55e';p.forEach(v=>x.fillRect(v[0]*s,v[1]*s,s-1,s-1));x.fillStyle='#ef4444';x.fillRect(f[0]*s,f[1]*s,s-1,s-1);
9
+ x.fillStyle='#fff';x.fillText('Score '+sc,8,14);setTimeout(tick,sp)}tick();
10
+ </script>
11
+
@@ -0,0 +1,14 @@
1
+ <!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Mini Tetris</title>
2
+ <style>body{margin:0;display:grid;place-items:center;height:100vh;background:#0b1020;color:#fff;font-family:system-ui}canvas{background:#111;border:1px solid #333}</style>
3
+ <canvas id=c width=200 height=400></canvas><script>
4
+ const W=10,H=20,S=20,c=document.getElementById('c'),x=c.getContext('2d');let b=[...Array(H)].map(()=>Array(W).fill(0)),p={x:3,y:0,m:[[1,1],[1,1]]},t=0;
5
+ onkeydown=e=>{if(e.key==='ArrowLeft')p.x--;if(e.key==='ArrowRight')p.x++;if(e.key==='ArrowDown')p.y++;};
6
+ function coll(){for(let y=0;y<p.m.length;y++)for(let x0=0;x0<p.m[y].length;x0++)if(p.m[y][x0]&&(b[p.y+y]?.[p.x+x0]??1))return 1;return 0}
7
+ function merge(){for(let y=0;y<p.m.length;y++)for(let x0=0;x0<p.m[y].length;x0++)if(p.m[y][x0])b[p.y+y][p.x+x0]=1}
8
+ function line(){for(let y=H-1;y>=0;y--)if(b[y].every(v=>v)){b.splice(y,1);b.unshift(Array(W).fill(0));y++}}
9
+ function spawn(){p={x:Math.floor(Math.random()*7),y:0,m:Math.random()<.5?[[1,1,1],[0,1,0]]:[[1,1],[1,1]]};if(coll())b=[...Array(H)].map(()=>Array(W).fill(0))}
10
+ spawn();function draw(){x.fillStyle='#111';x.fillRect(0,0,c.width,c.height);x.fillStyle='#3b82f6';for(let y=0;y<H;y++)for(let x0=0;x0<W;x0++)if(b[y][x0])x.fillRect(x0*S,y*S,S-1,S-1);
11
+ x.fillStyle='#f59e0b';for(let y=0;y<p.m.length;y++)for(let x0=0;x0<p.m[y].length;x0++)if(p.m[y][x0])x.fillRect((p.x+x0)*S,(p.y+y)*S,S-1,S-1)}
12
+ function loop(ts){if(ts-t>450){p.y++;if(coll()){p.y--;merge();line();spawn()}t=ts}draw();requestAnimationFrame(loop)}requestAnimationFrame(loop);
13
+ </script>
14
+
@@ -0,0 +1,8 @@
1
+ <!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Whack A Dot</title>
2
+ <style>body{margin:0;display:grid;place-items:center;height:100vh;background:#111827;color:#fff;font-family:system-ui}.g{display:grid;grid-template:repeat(3,90px)/repeat(3,90px);gap:8px}.c{border-radius:14px;background:#1f2937;display:grid;place-items:center;cursor:pointer}</style>
3
+ <h1 id=t>Whack A Dot - 0</h1><div class=g id=g></div><script>
4
+ let s=0,a=-1;for(let i=0;i<9;i++){const d=document.createElement('button');d.className='c';d.onclick=()=>{if(i===a){s++;t.textContent='Whack A Dot - '+s;pick()}};g.appendChild(d)}
5
+ function pick(){a=Math.random()*9|0;[...g.children].forEach((el,i)=>el.textContent=i===a?'●':'');}
6
+ setInterval(()=>pick(),700);pick();
7
+ </script>
8
+
package/cdn/go.js ADDED
@@ -0,0 +1,126 @@
1
+ function createRandomBackground() {
2
+ const container = document.getElementById('bg-characters-container');
3
+ if (!container) return;
4
+ for (let i = 0; i < 20; i++) {
5
+ const img = document.createElement('img');
6
+ const randNum = Math.floor(Math.random() * 14) + 1;
7
+ img.src = `https://cdn.jsdelivr.net/gh/sllkx/icons@main/char/${randNum}.png`;
8
+ img.className = "absolute select-none opacity-20 pointer-events-none transition-opacity duration-500";
9
+ const randSize = Math.floor(Math.random() * 90) + 40;
10
+ img.style.width = `${randSize}px`;
11
+ img.style.height = 'auto';
12
+ img.style.top = `${Math.random() * 100}%`;
13
+ img.style.left = `${Math.random() * 100}%`;
14
+ img.style.transform = `translate(-50%, -50%) rotate(${Math.floor(Math.random() * 360)}deg)`;
15
+ container.appendChild(img);
16
+ }
17
+ }
18
+ window.addEventListener('DOMContentLoaded', createRandomBackground);
19
+
20
+ const userLang = <?php echo json_encode($user_lang); ?>;
21
+ const cheeringMessages = {
22
+ ko: { r1: "시작! 클릭해 속도 올리기 (TAP!)", r2: "절반 달성! 계속 탭하기 (TAP!)", r3: "거의 다 왔어요! 힘내세요 (TAP!)", r4: "마지막 스퍼트! 조금만 더 (TAP!)", r5: "이동 준비 완료!", r6: "★ 완료! 클릭해서 이동하기 (Go!) ★" },
23
+ en: { r1: "Start! Tap to speed up (TAP!)", r2: "Halfway! Keep tapping (TAP!)", r3: "Almost there! Keep going (TAP!)", r4: "Final sprint! Just a bit more (TAP!)", r5: "Ready to go!", r6: "★ Complete! Click to Proceed (Go!) ★" },
24
+ es: { r1: "¡Iniciar! Toca para acelerar (TAP!)", r2: "¡Mitad! Sigue tocando (TAP!)", r3: "¡Casi listo! ¡Vamos! (TAP!)", r4: "¡Esprint final! Un poco más (TAP!)", r5: "¡Listo para ir!", r6: "★ ¡Completo! Haz clic para continuar ★" },
25
+ hi: { r1: "शुरू! गति बढ़ाएं (TAP!)", r2: "आधा रास्ता! टैप करते रहें (TAP!)", r3: "बस पहुंचने वाले हैं! चलो! (TAP!)", r4: "अंतिम प्रयास! बस थोड़ा और (TAP!)", r5: "जाने के लिए तैयार!", r6: "★ पूरा हुआ! आगे बढ़ने के लिए क्लिक करें ★" },
26
+ ja: { r1: "開始!タップで高速化 (TAP!)", r2: "半分達成!タップ継続! (TAP!)", r3: "あと少し!がんばって! (TAP!)", r4: "ラストスパート!一息! (TAP!)", r5: "移動します!", r6: "★ 完了!クリックして移動する ★" }
27
+ };
28
+
29
+ function getCheeringText(pct) {
30
+ const langData = cheeringMessages[userLang] || cheeringMessages['en'];
31
+ const msg = pct <= 30 ? langData.r1 : pct <= 60 ? langData.r2 : pct <= 90 ? langData.r3 : pct < 100 ? langData.r4 : langData.r5;
32
+ return `${pct}% - ${msg}`;
33
+ }
34
+
35
+ function getFinalClickText() {
36
+ return (cheeringMessages[userLang] || cheeringMessages['en']).r6;
37
+ }
38
+
39
+ const targetUrl = <?php echo json_encode($target_url); ?>;
40
+ let progress = 0, isReadyToRedirect = false, isCompleted = false, isCooldown = false, score = 0;
41
+
42
+ const progressBar = document.getElementById('progress-bar');
43
+ const progressText = document.getElementById('progress-text');
44
+ const loadingTrigger = document.getElementById('loading-trigger');
45
+ const characterGame = document.getElementById('character-game');
46
+ const gameCharImg = document.getElementById('game-char-img');
47
+ const cardContainer = document.getElementById('card-container');
48
+ const scoreBoard = document.getElementById('game-score');
49
+
50
+ function updateProgress(value) {
51
+ if (isCompleted || isReadyToRedirect) return;
52
+ progress = Math.min(100, Math.max(0, value));
53
+ progressBar.style.width = progress + '%';
54
+ progressText.textContent = getCheeringText(progress);
55
+
56
+ if (progress >= 100) {
57
+ isReadyToRedirect = true;
58
+ clearInterval(autoIncrementInterval);
59
+ progressBar.classList.add('bg-emerald-500');
60
+ progressBar.classList.remove('bg-white');
61
+ loadingTrigger.classList.remove('opacity-40', 'cursor-not-allowed');
62
+ loadingTrigger.classList.add('cursor-pointer', 'animate-pulse');
63
+ progressText.textContent = getFinalClickText();
64
+ }
65
+ }
66
+
67
+ function handleFinalRedirect() {
68
+ if (isCompleted) return;
69
+ isCompleted = true;
70
+ window.open('https://omg10.com/4/10628199', '_blank');
71
+ window.location.href = targetUrl;
72
+ }
73
+
74
+ function spawnFloatingEffect(triggerElement, text) {
75
+ const rect = triggerElement.getBoundingClientRect();
76
+ const cardRect = cardContainer.getBoundingClientRect();
77
+ const x = rect.left - cardRect.left + (rect.width / 2);
78
+ const top = rect.top - cardRect.top;
79
+ const effectEl = document.createElement('div');
80
+ effectEl.textContent = text;
81
+ effectEl.className = "absolute text-amber-300 font-black text-xs pointer-events-none select-none z-50 transition-all duration-500 ease-out";
82
+ effectEl.style.left = `${x}px`;
83
+ effectEl.style.top = `${top}px`;
84
+ effectEl.style.transform = 'translateX(-50%)';
85
+ cardContainer.appendChild(effectEl);
86
+ effectEl.offsetHeight;
87
+ effectEl.style.top = `${top - 40}px`;
88
+ effectEl.style.opacity = '0';
89
+ setTimeout(() => effectEl.remove(), 500);
90
+ }
91
+
92
+ updateProgress(0);
93
+
94
+ const autoIncrementInterval = setInterval(() => {
95
+ if (!isReadyToRedirect) updateProgress(progress + 1);
96
+ }, 3000);
97
+
98
+ loadingTrigger.addEventListener('click', () => {
99
+ if (isReadyToRedirect) {
100
+ handleFinalRedirect();
101
+ return;
102
+ }
103
+ if (isCompleted || isCooldown) return;
104
+ isCooldown = true;
105
+ loadingTrigger.classList.add('opacity-40', 'cursor-not-allowed');
106
+ loadingTrigger.classList.remove('cursor-pointer');
107
+ spawnFloatingEffect(loadingTrigger, '+3%');
108
+ updateProgress(progress + 3);
109
+ setTimeout(() => {
110
+ isCooldown = false;
111
+ if (!isReadyToRedirect) {
112
+ loadingTrigger.classList.remove('opacity-40', 'cursor-not-allowed');
113
+ loadingTrigger.classList.add('cursor-pointer');
114
+ }
115
+ }, 2000);
116
+ });
117
+
118
+ characterGame.addEventListener('click', () => {
119
+ if (isCompleted) return;
120
+ score++;
121
+ scoreBoard.textContent = score;
122
+ gameCharImg.classList.add('animate-squish');
123
+ gameCharImg.addEventListener('animationend', () => gameCharImg.classList.remove('animate-squish'), { once: true });
124
+ gameCharImg.src = `https://cdn.jsdelivr.net/gh/sllkx/icons@main/char/${Math.floor(Math.random() * 14) + 1}.png`;
125
+ spawnFloatingEffect(characterGame, '+1');
126
+ });