@funnycaptcha/mini-game 0.2.0 → 0.6.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/dist/index.js CHANGED
@@ -28,7 +28,8 @@ var STR = {
28
28
  time: "\u5269\u4F59",
29
29
  sec: "\u79D2",
30
30
  success: "\u9A8C\u8BC1\u6210\u529F\uFF01\u4F60\u679C\u7136\u662F\u4EBA\u7C7B",
31
- fail: "\u65F6\u95F4\u5230\uFF0C\u6CA1\u8FBE\u5230\u76EE\u6807\u5206\u6570"
31
+ fail: "\u65F6\u95F4\u5230\uFF0C\u6CA1\u8FBE\u5230\u76EE\u6807\u5206\u6570",
32
+ restart: "\u91CD\u65B0\u5F00\u59CB"
32
33
  },
33
34
  en: {
34
35
  title: "Whack-a-Mole",
@@ -37,12 +38,16 @@ var STR = {
37
38
  time: "Time",
38
39
  sec: "s",
39
40
  success: "Verified! You are human.",
40
- fail: "Time's up, target not reached"
41
+ fail: "Time's up, target not reached",
42
+ restart: "Restart"
41
43
  }
42
44
  };
43
45
  var HOLE_COUNT = 9;
46
+ var MOLE_MIN_DELAY = 600;
47
+ var MOLE_MAX_DELAY = 1200;
44
48
  function createMiniGameInstance(container, config) {
45
49
  const t = STR[config.locale];
50
+ const theme = config.theme ?? "light";
46
51
  let current;
47
52
  let listeners = [];
48
53
  let startTime = Date.now();
@@ -59,7 +64,7 @@ function createMiniGameInstance(container, config) {
59
64
  countdownTimer = null;
60
65
  }
61
66
  if (moleTimer) {
62
- clearInterval(moleTimer);
67
+ clearTimeout(moleTimer);
63
68
  moleTimer = null;
64
69
  }
65
70
  if (hideTimer) {
@@ -107,6 +112,14 @@ function createMiniGameInstance(container, config) {
107
112
  if (activeHole === next) activeHole = -1;
108
113
  }, 700);
109
114
  }
115
+ function scheduleMole() {
116
+ if (ended) return;
117
+ const delay = MOLE_MIN_DELAY + Math.random() * (MOLE_MAX_DELAY - MOLE_MIN_DELAY);
118
+ moleTimer = setTimeout(() => {
119
+ showMole();
120
+ scheduleMole();
121
+ }, delay);
122
+ }
110
123
  function startGame() {
111
124
  clearTimers();
112
125
  score = 0;
@@ -128,8 +141,8 @@ function createMiniGameInstance(container, config) {
128
141
  void endGame(verifyScore(current, score));
129
142
  }
130
143
  }, 100);
131
- moleTimer = setInterval(showMole, 850);
132
144
  showMole();
145
+ scheduleMole();
133
146
  }
134
147
  function render() {
135
148
  current = generateChallenge();
@@ -143,19 +156,25 @@ function createMiniGameInstance(container, config) {
143
156
  ).join("");
144
157
  container.innerHTML = `
145
158
  <style>
146
- .fc-mini-game{font-family:-apple-system,system-ui,sans-serif;width:340px;padding:16px;box-sizing:border-box}
147
- .fc-mini-game-title{font-size:15px;color:#333;margin-bottom:4px;text-align:center;font-weight:600}
148
- .fc-mini-game-rule{font-size:12px;color:#888;margin-bottom:10px;text-align:center}
149
- .fc-mini-game-hud{display:flex;justify-content:space-between;font-size:13px;color:#555;margin-bottom:10px}
150
- .fc-mini-game-hud b{color:#4a90d9}
151
- .fc-mini-game-board{display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(3,1fr);gap:8px;width:300px;height:300px;margin:0 auto;background:#e8f5e9;border:1px solid #c8e6c9;border-radius:10px;padding:8px;box-sizing:border-box}
159
+ .fc-mini-game{font-family:-apple-system,system-ui,sans-serif;max-width:360px;width:100%;padding:16px;box-sizing:border-box}
160
+ .fc-mini-game[data-theme="light"]{--fc-bg:#ffffff;--fc-surface:#f6f7f9;--fc-text:#0f172a;--fc-text-soft:#64748b;--fc-border:#e2e8f0;--fc-accent:#6366f1;--fc-accent-soft:#eef2ff;--fc-success:#16a34a;--fc-danger:#dc2626}
161
+ .fc-mini-game[data-theme="dark"]{--fc-bg:#1e2544;--fc-surface:#171c36;--fc-text:#e5e9f0;--fc-text-soft:#94a3b8;--fc-border:#2a3358;--fc-accent:#818cf8;--fc-accent-soft:#252b5c;--fc-success:#4ade80;--fc-danger:#f87171}
162
+ .fc-mini-game{background:var(--fc-bg);border:1px solid var(--fc-border);border-radius:10px;color:var(--fc-text)}
163
+ .fc-mini-game-title{font-size:15px;color:var(--fc-text);margin-bottom:4px;text-align:center;font-weight:600}
164
+ .fc-mini-game-rule{font-size:12px;color:var(--fc-text-soft);margin-bottom:10px;text-align:center}
165
+ .fc-mini-game-hud{display:flex;justify-content:space-between;font-size:13px;color:var(--fc-text-soft);margin-bottom:10px}
166
+ .fc-mini-game-hud b{color:var(--fc-accent)}
167
+ .fc-mini-game-board{display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(3,1fr);gap:8px;width:300px;height:300px;margin:0 auto;background:var(--fc-surface);border:1px solid var(--fc-border);border-radius:10px;padding:8px;box-sizing:border-box}
152
168
  .fc-mini-game-hole{position:relative;background:radial-gradient(circle at 50% 60%,#6d4c41,#4e342e);border-radius:50%;display:flex;align-items:flex-end;justify-content:center;overflow:hidden;cursor:pointer;user-select:none}
153
169
  .fc-mini-game-hole:hover{background:radial-gradient(circle at 50% 60%,#7d5c51,#5e443e)}
154
170
  .fc-mini-game-mole{font-size:34px;line-height:1;transform:translateY(120%);transition:transform .12s ease-out;pointer-events:none}
155
171
  .fc-mini-game-mole-active .fc-mini-game-mole{transform:translateY(0)}
156
- .fc-mini-game-msg{font-size:13px;min-height:18px;text-align:center;margin-top:12px;color:#2e7d32}
172
+ .fc-mini-game-msg{font-size:13px;min-height:18px;text-align:center;margin-top:12px;color:var(--fc-success)}
173
+ .fc-mini-game-row{display:flex;justify-content:center;margin-top:8px}
174
+ .fc-mini-game-restart{padding:4px 12px;font-size:12px;border:1px solid var(--fc-border);background:var(--fc-surface);color:var(--fc-text-soft);border-radius:6px;cursor:pointer}
175
+ .fc-mini-game-restart:hover{border-color:var(--fc-accent);color:var(--fc-accent)}
157
176
  </style>
158
- <div class="fc-mini-game">
177
+ <div class="fc-mini-game" data-theme="${theme}">
159
178
  <div class="fc-mini-game-title">${t.title}</div>
160
179
  <div class="fc-mini-game-rule">${t.rule}</div>
161
180
  <div class="fc-mini-game-hud">
@@ -164,8 +183,13 @@ function createMiniGameInstance(container, config) {
164
183
  </div>
165
184
  <div class="fc-mini-game-board">${holes}</div>
166
185
  <div class="fc-mini-game-msg"></div>
186
+ <div class="fc-mini-game-row">
187
+ <button class="fc-mini-game-restart">${t.restart}</button>
188
+ </div>
167
189
  </div>
168
190
  `;
191
+ const restartBtn = container.querySelector(".fc-mini-game-restart");
192
+ restartBtn.addEventListener("click", startGame);
169
193
  container.querySelectorAll(".fc-mini-game-hole").forEach((el) => {
170
194
  el.addEventListener("click", () => {
171
195
  if (ended) return;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/render.ts","../src/challenge.ts","../src/verifier.ts"],"sourcesContent":["import { defineCaptcha, type CaptchaConfig, type CaptchaPlugin } from '@funnycaptcha/core';\nimport { createMiniGameInstance } from './render.js';\n\nexport * from './challenge.js';\nexport * from './verifier.js';\n\nexport const miniGamePlugin: CaptchaPlugin = {\n id: 'mini-game',\n category: 'game',\n create: createMiniGameInstance,\n describe: (locale) => ({\n name: locale === 'zh' ? '打地鼠' : 'Whack-a-Mole',\n description: locale === 'zh'\n ? '30 秒内打够 10 只地鼠,简单又解压'\n : 'Whack 10 moles in 30 seconds. Simple and fun.',\n tags: ['game', 'whack-a-mole', 'timed'],\n }),\n};\n\ndefineCaptcha(miniGamePlugin);\n\nexport function mountMiniGame(container: HTMLElement, config: CaptchaConfig) {\n return createMiniGameInstance(container, config);\n}\n","import type { CaptchaConfig, CaptchaInstance, CaptchaResult } from '@funnycaptcha/core';\nimport { hashProof } from '@funnycaptcha/core';\nimport { generateChallenge, proofInput, verifyScore, type MiniGameChallenge } from './challenge.js';\n\nconst STR = {\n zh: {\n title: '打地鼠',\n rule: '30 秒内打到 10 分即通过',\n score: '分数',\n time: '剩余',\n sec: '秒',\n success: '验证成功!你果然是人类',\n fail: '时间到,没达到目标分数',\n },\n en: {\n title: 'Whack-a-Mole',\n rule: 'Score 10 in 30s to pass',\n score: 'Score',\n time: 'Time',\n sec: 's',\n success: 'Verified! You are human.',\n fail: \"Time's up, target not reached\",\n },\n};\n\n// 3x3 共 9 个洞\nconst HOLE_COUNT = 9;\n\nexport function createMiniGameInstance(\n container: HTMLElement,\n config: CaptchaConfig,\n): CaptchaInstance {\n const t = STR[config.locale];\n let current: MiniGameChallenge;\n let listeners: ((r: CaptchaResult) => void)[] = [];\n let startTime = Date.now();\n let score = 0;\n let ended = false;\n let activeHole = -1;\n let remainingMs = 0;\n // 三个定时器:倒计时、地鼠出现、单只地鼠自动消失\n let countdownTimer: ReturnType<typeof setInterval> | null = null;\n let moleTimer: ReturnType<typeof setInterval> | null = null;\n let hideTimer: ReturnType<typeof setTimeout> | null = null;\n\n function clearTimers() {\n if (countdownTimer) { clearInterval(countdownTimer); countdownTimer = null; }\n if (moleTimer) { clearInterval(moleTimer); moleTimer = null; }\n if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; }\n }\n\n // 结束游戏:达到目标即成功,否则失败\n async function endGame(success: boolean) {\n if (ended) return;\n ended = true;\n clearTimers();\n const holes = container.querySelectorAll<HTMLElement>('.fc-mini-game-hole');\n holes.forEach(h => h.classList.remove('fc-mini-game-mole-active'));\n const msg = container.querySelector('.fc-mini-game-msg');\n if (success) {\n const result: CaptchaResult = {\n success: true,\n proof: await hashProof(proofInput(current)),\n duration: Date.now() - startTime,\n metadata: { score, targetScore: current.targetScore },\n };\n if (msg) msg.textContent = t.success;\n config.onVerify?.(result);\n listeners.forEach(cb => cb(result));\n } else {\n if (msg) msg.textContent = t.fail;\n }\n }\n\n // 随机在一个洞里弹出地鼠\n function showMole() {\n if (ended) return;\n const holes = container.querySelectorAll<HTMLElement>('.fc-mini-game-hole');\n holes.forEach(h => h.classList.remove('fc-mini-game-mole-active'));\n if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; }\n const next = Math.floor(Math.random() * HOLE_COUNT);\n activeHole = next;\n const hole = holes[next];\n if (hole) hole.classList.add('fc-mini-game-mole-active');\n // 一段时间后地鼠自动缩回\n hideTimer = setTimeout(() => {\n if (ended) return;\n const cur = container.querySelectorAll<HTMLElement>('.fc-mini-game-hole')[next];\n if (cur) cur.classList.remove('fc-mini-game-mole-active');\n if (activeHole === next) activeHole = -1;\n }, 700);\n }\n\n function startGame() {\n clearTimers();\n score = 0;\n ended = false;\n activeHole = -1;\n remainingMs = current.timeLimit;\n startTime = Date.now();\n const scoreEl = container.querySelector('.fc-mini-game-score');\n const timeEl = container.querySelector('.fc-mini-game-time');\n const msg = container.querySelector('.fc-mini-game-msg');\n if (scoreEl) scoreEl.textContent = `0 / ${current.targetScore}`;\n if (timeEl) timeEl.textContent = `${Math.ceil(remainingMs / 1000)}${t.sec}`;\n if (msg) msg.textContent = '';\n // 倒计时:每 100ms 更新\n countdownTimer = setInterval(() => {\n if (ended) return;\n remainingMs -= 100;\n if (timeEl) timeEl.textContent = `${Math.max(0, Math.ceil(remainingMs / 1000))}${t.sec}`;\n if (remainingMs <= 0) {\n void endGame(verifyScore(current, score));\n }\n }, 100);\n // 地鼠出现:每 850ms 弹一只\n moleTimer = setInterval(showMole, 850);\n showMole();\n }\n\n function render() {\n current = generateChallenge();\n score = 0;\n ended = false;\n activeHole = -1;\n clearTimers();\n const holes = Array.from({ length: HOLE_COUNT }, (_, i) =>\n `<div class=\"fc-mini-game-hole\" data-idx=\"${i}\"><span class=\"fc-mini-game-mole\">🐹</span></div>`,\n ).join('');\n container.innerHTML = `\n <style>\n .fc-mini-game{font-family:-apple-system,system-ui,sans-serif;width:340px;padding:16px;box-sizing:border-box}\n .fc-mini-game-title{font-size:15px;color:#333;margin-bottom:4px;text-align:center;font-weight:600}\n .fc-mini-game-rule{font-size:12px;color:#888;margin-bottom:10px;text-align:center}\n .fc-mini-game-hud{display:flex;justify-content:space-between;font-size:13px;color:#555;margin-bottom:10px}\n .fc-mini-game-hud b{color:#4a90d9}\n .fc-mini-game-board{display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(3,1fr);gap:8px;width:300px;height:300px;margin:0 auto;background:#e8f5e9;border:1px solid #c8e6c9;border-radius:10px;padding:8px;box-sizing:border-box}\n .fc-mini-game-hole{position:relative;background:radial-gradient(circle at 50% 60%,#6d4c41,#4e342e);border-radius:50%;display:flex;align-items:flex-end;justify-content:center;overflow:hidden;cursor:pointer;user-select:none}\n .fc-mini-game-hole:hover{background:radial-gradient(circle at 50% 60%,#7d5c51,#5e443e)}\n .fc-mini-game-mole{font-size:34px;line-height:1;transform:translateY(120%);transition:transform .12s ease-out;pointer-events:none}\n .fc-mini-game-mole-active .fc-mini-game-mole{transform:translateY(0)}\n .fc-mini-game-msg{font-size:13px;min-height:18px;text-align:center;margin-top:12px;color:#2e7d32}\n </style>\n <div class=\"fc-mini-game\">\n <div class=\"fc-mini-game-title\">${t.title}</div>\n <div class=\"fc-mini-game-rule\">${t.rule}</div>\n <div class=\"fc-mini-game-hud\">\n <div>${t.score}: <b class=\"fc-mini-game-score\">0 / ${current.targetScore}</b></div>\n <div>${t.time}: <b class=\"fc-mini-game-time\">${current.timeLimit / 1000}${t.sec}</b></div>\n </div>\n <div class=\"fc-mini-game-board\">${holes}</div>\n <div class=\"fc-mini-game-msg\"></div>\n </div>\n `;\n\n container.querySelectorAll<HTMLElement>('.fc-mini-game-hole').forEach(el => {\n el.addEventListener('click', () => {\n if (ended) return;\n const idx = Number(el.dataset.idx);\n // 仅当点击的是当前有地鼠的洞时计分\n if (idx === activeHole && el.classList.contains('fc-mini-game-mole-active')) {\n score += 1;\n el.classList.remove('fc-mini-game-mole-active');\n activeHole = -1;\n if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; }\n const scoreEl = container.querySelector('.fc-mini-game-score');\n if (scoreEl) scoreEl.textContent = `${score} / ${current.targetScore}`;\n if (verifyScore(current, score)) {\n void endGame(true);\n }\n }\n });\n });\n\n startGame();\n }\n\n return {\n mount: () => render(),\n reset: () => render(),\n destroy: () => { clearTimers(); container.innerHTML = ''; listeners = []; },\n onResult: cb => listeners.push(cb),\n };\n}\n","// 打地鼠题目:限时内击中地鼠达到目标分数即通过\nexport interface MiniGameChallenge {\n // 目标分数(达到即通过)\n targetScore: number;\n // 游戏令牌,用于构造 proof\n gameToken: string;\n // 时间限制(毫秒)\n timeLimit: number;\n}\n\n// 生成题目:目标 10 分,限时 30 秒,随机令牌\nexport function generateChallenge(): MiniGameChallenge {\n return {\n targetScore: 10,\n timeLimit: 30000,\n gameToken: crypto.randomUUID(),\n };\n}\n\n// 校验用户得分是否达到目标分数\nexport function verifyScore(c: MiniGameChallenge, userScore: number): boolean {\n return userScore >= c.targetScore;\n}\n\n// 构造 proof 输入串:gameToken:targetScore\nexport function proofInput(c: MiniGameChallenge): string {\n return `${c.gameToken}:${c.targetScore}`;\n}\n","import { hashProof } from '@funnycaptcha/core';\nimport { generateChallenge, proofInput, type MiniGameChallenge } from './challenge.js';\n\nexport function issueMiniGameChallenge(): { challenge: MiniGameChallenge; payload: unknown } {\n const challenge = generateChallenge();\n return {\n challenge,\n payload: { targetScore: challenge.targetScore, timeLimit: challenge.timeLimit },\n };\n}\n\nexport async function verifyMiniGameProof(\n challenge: MiniGameChallenge,\n proof: string,\n): Promise<boolean> {\n const expected = await hashProof(proofInput(challenge));\n return expected === proof;\n}\n"],"mappings":"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","names":["hashProof","hashProof"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/render.ts","../src/challenge.ts","../src/verifier.ts"],"sourcesContent":["import { defineCaptcha, type CaptchaConfig, type CaptchaPlugin } from '@funnycaptcha/core';\nimport { createMiniGameInstance } from './render.js';\n\nexport * from './challenge.js';\nexport * from './verifier.js';\n\nexport const miniGamePlugin: CaptchaPlugin = {\n id: 'mini-game',\n category: 'game',\n create: createMiniGameInstance,\n describe: (locale) => ({\n name: locale === 'zh' ? '打地鼠' : 'Whack-a-Mole',\n description: locale === 'zh'\n ? '30 秒内打够 10 只地鼠,简单又解压'\n : 'Whack 10 moles in 30 seconds. Simple and fun.',\n tags: ['game', 'whack-a-mole', 'timed'],\n }),\n};\n\ndefineCaptcha(miniGamePlugin);\n\nexport function mountMiniGame(container: HTMLElement, config: CaptchaConfig) {\n return createMiniGameInstance(container, config);\n}\n","import type { CaptchaConfig, CaptchaInstance, CaptchaResult } from '@funnycaptcha/core';\nimport { hashProof } from '@funnycaptcha/core';\nimport { generateChallenge, proofInput, verifyScore, type MiniGameChallenge } from './challenge.js';\n\nconst STR = {\n zh: {\n title: '打地鼠',\n rule: '30 秒内打到 10 分即通过',\n score: '分数',\n time: '剩余',\n sec: '秒',\n success: '验证成功!你果然是人类',\n fail: '时间到,没达到目标分数',\n restart: '重新开始',\n },\n en: {\n title: 'Whack-a-Mole',\n rule: 'Score 10 in 30s to pass',\n score: 'Score',\n time: 'Time',\n sec: 's',\n success: 'Verified! You are human.',\n fail: \"Time's up, target not reached\",\n restart: 'Restart',\n },\n};\n\n// 3x3 共 9 个洞\nconst HOLE_COUNT = 9;\n// 地鼠出现间隔范围(ms)\nconst MOLE_MIN_DELAY = 600;\nconst MOLE_MAX_DELAY = 1200;\n\nexport function createMiniGameInstance(\n container: HTMLElement,\n config: CaptchaConfig,\n): CaptchaInstance {\n const t = STR[config.locale];\n const theme = config.theme ?? 'light';\n let current: MiniGameChallenge;\n let listeners: ((r: CaptchaResult) => void)[] = [];\n let startTime = Date.now();\n let score = 0;\n let ended = false;\n let activeHole = -1;\n let remainingMs = 0;\n // 三个定时器:倒计时、地鼠出现、单只地鼠自动消失\n let countdownTimer: ReturnType<typeof setInterval> | null = null;\n let moleTimer: ReturnType<typeof setTimeout> | null = null;\n let hideTimer: ReturnType<typeof setTimeout> | null = null;\n\n function clearTimers() {\n if (countdownTimer) { clearInterval(countdownTimer); countdownTimer = null; }\n if (moleTimer) { clearTimeout(moleTimer); moleTimer = null; }\n if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; }\n }\n\n // 结束游戏:达到目标即成功,否则失败\n async function endGame(success: boolean) {\n if (ended) return;\n ended = true;\n clearTimers();\n const holes = container.querySelectorAll<HTMLElement>('.fc-mini-game-hole');\n holes.forEach(h => h.classList.remove('fc-mini-game-mole-active'));\n const msg = container.querySelector('.fc-mini-game-msg');\n if (success) {\n const result: CaptchaResult = {\n success: true,\n proof: await hashProof(proofInput(current)),\n duration: Date.now() - startTime,\n metadata: { score, targetScore: current.targetScore },\n };\n if (msg) msg.textContent = t.success;\n config.onVerify?.(result);\n listeners.forEach(cb => cb(result));\n } else {\n if (msg) msg.textContent = t.fail;\n }\n }\n\n // 随机在一个洞里弹出地鼠\n function showMole() {\n if (ended) return;\n const holes = container.querySelectorAll<HTMLElement>('.fc-mini-game-hole');\n holes.forEach(h => h.classList.remove('fc-mini-game-mole-active'));\n if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; }\n const next = Math.floor(Math.random() * HOLE_COUNT);\n activeHole = next;\n const hole = holes[next];\n if (hole) hole.classList.add('fc-mini-game-mole-active');\n // 一段时间后地鼠自动缩回\n hideTimer = setTimeout(() => {\n if (ended) return;\n const cur = container.querySelectorAll<HTMLElement>('.fc-mini-game-hole')[next];\n if (cur) cur.classList.remove('fc-mini-game-mole-active');\n if (activeHole === next) activeHole = -1;\n }, 700);\n }\n\n // 以 600-1200ms 随机间隔循环弹地鼠\n function scheduleMole() {\n if (ended) return;\n const delay = MOLE_MIN_DELAY + Math.random() * (MOLE_MAX_DELAY - MOLE_MIN_DELAY);\n moleTimer = setTimeout(() => {\n showMole();\n scheduleMole();\n }, delay);\n }\n\n function startGame() {\n clearTimers();\n score = 0;\n ended = false;\n activeHole = -1;\n remainingMs = current.timeLimit;\n startTime = Date.now();\n const scoreEl = container.querySelector('.fc-mini-game-score');\n const timeEl = container.querySelector('.fc-mini-game-time');\n const msg = container.querySelector('.fc-mini-game-msg');\n if (scoreEl) scoreEl.textContent = `0 / ${current.targetScore}`;\n if (timeEl) timeEl.textContent = `${Math.ceil(remainingMs / 1000)}${t.sec}`;\n if (msg) msg.textContent = '';\n // 倒计时:每 100ms 更新\n countdownTimer = setInterval(() => {\n if (ended) return;\n remainingMs -= 100;\n if (timeEl) timeEl.textContent = `${Math.max(0, Math.ceil(remainingMs / 1000))}${t.sec}`;\n if (remainingMs <= 0) {\n void endGame(verifyScore(current, score));\n }\n }, 100);\n // 地鼠出现:随机间隔 600-1200ms\n showMole();\n scheduleMole();\n }\n\n function render() {\n current = generateChallenge();\n score = 0;\n ended = false;\n activeHole = -1;\n clearTimers();\n const holes = Array.from({ length: HOLE_COUNT }, (_, i) =>\n `<div class=\"fc-mini-game-hole\" data-idx=\"${i}\"><span class=\"fc-mini-game-mole\">🐹</span></div>`,\n ).join('');\n container.innerHTML = `\n <style>\n .fc-mini-game{font-family:-apple-system,system-ui,sans-serif;max-width:360px;width:100%;padding:16px;box-sizing:border-box}\n .fc-mini-game[data-theme=\"light\"]{--fc-bg:#ffffff;--fc-surface:#f6f7f9;--fc-text:#0f172a;--fc-text-soft:#64748b;--fc-border:#e2e8f0;--fc-accent:#6366f1;--fc-accent-soft:#eef2ff;--fc-success:#16a34a;--fc-danger:#dc2626}\n .fc-mini-game[data-theme=\"dark\"]{--fc-bg:#1e2544;--fc-surface:#171c36;--fc-text:#e5e9f0;--fc-text-soft:#94a3b8;--fc-border:#2a3358;--fc-accent:#818cf8;--fc-accent-soft:#252b5c;--fc-success:#4ade80;--fc-danger:#f87171}\n .fc-mini-game{background:var(--fc-bg);border:1px solid var(--fc-border);border-radius:10px;color:var(--fc-text)}\n .fc-mini-game-title{font-size:15px;color:var(--fc-text);margin-bottom:4px;text-align:center;font-weight:600}\n .fc-mini-game-rule{font-size:12px;color:var(--fc-text-soft);margin-bottom:10px;text-align:center}\n .fc-mini-game-hud{display:flex;justify-content:space-between;font-size:13px;color:var(--fc-text-soft);margin-bottom:10px}\n .fc-mini-game-hud b{color:var(--fc-accent)}\n .fc-mini-game-board{display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(3,1fr);gap:8px;width:300px;height:300px;margin:0 auto;background:var(--fc-surface);border:1px solid var(--fc-border);border-radius:10px;padding:8px;box-sizing:border-box}\n .fc-mini-game-hole{position:relative;background:radial-gradient(circle at 50% 60%,#6d4c41,#4e342e);border-radius:50%;display:flex;align-items:flex-end;justify-content:center;overflow:hidden;cursor:pointer;user-select:none}\n .fc-mini-game-hole:hover{background:radial-gradient(circle at 50% 60%,#7d5c51,#5e443e)}\n .fc-mini-game-mole{font-size:34px;line-height:1;transform:translateY(120%);transition:transform .12s ease-out;pointer-events:none}\n .fc-mini-game-mole-active .fc-mini-game-mole{transform:translateY(0)}\n .fc-mini-game-msg{font-size:13px;min-height:18px;text-align:center;margin-top:12px;color:var(--fc-success)}\n .fc-mini-game-row{display:flex;justify-content:center;margin-top:8px}\n .fc-mini-game-restart{padding:4px 12px;font-size:12px;border:1px solid var(--fc-border);background:var(--fc-surface);color:var(--fc-text-soft);border-radius:6px;cursor:pointer}\n .fc-mini-game-restart:hover{border-color:var(--fc-accent);color:var(--fc-accent)}\n </style>\n <div class=\"fc-mini-game\" data-theme=\"${theme}\">\n <div class=\"fc-mini-game-title\">${t.title}</div>\n <div class=\"fc-mini-game-rule\">${t.rule}</div>\n <div class=\"fc-mini-game-hud\">\n <div>${t.score}: <b class=\"fc-mini-game-score\">0 / ${current.targetScore}</b></div>\n <div>${t.time}: <b class=\"fc-mini-game-time\">${current.timeLimit / 1000}${t.sec}</b></div>\n </div>\n <div class=\"fc-mini-game-board\">${holes}</div>\n <div class=\"fc-mini-game-msg\"></div>\n <div class=\"fc-mini-game-row\">\n <button class=\"fc-mini-game-restart\">${t.restart}</button>\n </div>\n </div>\n `;\n const restartBtn = container.querySelector('.fc-mini-game-restart') as HTMLButtonElement;\n restartBtn.addEventListener('click', startGame);\n\n container.querySelectorAll<HTMLElement>('.fc-mini-game-hole').forEach(el => {\n el.addEventListener('click', () => {\n if (ended) return;\n const idx = Number(el.dataset.idx);\n // 仅当点击的是当前有地鼠的洞时计分\n if (idx === activeHole && el.classList.contains('fc-mini-game-mole-active')) {\n score += 1;\n el.classList.remove('fc-mini-game-mole-active');\n activeHole = -1;\n if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; }\n const scoreEl = container.querySelector('.fc-mini-game-score');\n if (scoreEl) scoreEl.textContent = `${score} / ${current.targetScore}`;\n if (verifyScore(current, score)) {\n void endGame(true);\n }\n }\n });\n });\n\n startGame();\n }\n\n return {\n mount: () => render(),\n reset: () => render(),\n destroy: () => { clearTimers(); container.innerHTML = ''; listeners = []; },\n onResult: cb => listeners.push(cb),\n };\n}\n","// 打地鼠题目:限时内击中地鼠达到目标分数即通过\nexport interface MiniGameChallenge {\n // 目标分数(达到即通过)\n targetScore: number;\n // 游戏令牌,用于构造 proof\n gameToken: string;\n // 时间限制(毫秒)\n timeLimit: number;\n}\n\n// 生成题目:目标 10 分,限时 30 秒,随机令牌\nexport function generateChallenge(): MiniGameChallenge {\n return {\n targetScore: 10,\n timeLimit: 30000,\n gameToken: crypto.randomUUID(),\n };\n}\n\n// 校验用户得分是否达到目标分数\nexport function verifyScore(c: MiniGameChallenge, userScore: number): boolean {\n return userScore >= c.targetScore;\n}\n\n// 构造 proof 输入串:gameToken:targetScore\nexport function proofInput(c: MiniGameChallenge): string {\n return `${c.gameToken}:${c.targetScore}`;\n}\n","import { hashProof } from '@funnycaptcha/core';\nimport { generateChallenge, proofInput, type MiniGameChallenge } from './challenge.js';\n\nexport function issueMiniGameChallenge(): { challenge: MiniGameChallenge; payload: unknown } {\n const challenge = generateChallenge();\n return {\n challenge,\n payload: { targetScore: challenge.targetScore, timeLimit: challenge.timeLimit },\n };\n}\n\nexport async function verifyMiniGameProof(\n challenge: MiniGameChallenge,\n proof: string,\n): Promise<boolean> {\n const expected = await hashProof(proofInput(challenge));\n return expected === proof;\n}\n"],"mappings":"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","names":["hashProof","hashProof"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnycaptcha/mini-game",
3
- "version": "0.2.0",
3
+ "version": "0.6.0",
4
4
  "description": "Whack-a-mole mini-game captcha — FunnyChapter",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -15,7 +15,7 @@
15
15
  "dist"
16
16
  ],
17
17
  "dependencies": {
18
- "@funnycaptcha/core": "0.2.0"
18
+ "@funnycaptcha/core": "0.6.0"
19
19
  },
20
20
  "publishConfig": {
21
21
  "access": "public",