sa2kit 2.0.1 → 2.0.2

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 (264) hide show
  1. package/README.md +1 -1
  2. package/dist/CollisionBalls-BpHufX3H.d.mts +41 -0
  3. package/dist/CollisionBalls-BpHufX3H.d.ts +41 -0
  4. package/dist/ConfigService-BxK06xP6.d.mts +262 -0
  5. package/dist/ConfigService-BxK06xP6.d.ts +262 -0
  6. package/dist/UniversalFileService-BpvbZitV.d.mts +139 -0
  7. package/dist/UniversalFileService-GsP6D3Rc.d.ts +139 -0
  8. package/dist/audioDetection/index.d.mts +449 -0
  9. package/dist/audioDetection/index.d.ts +449 -0
  10. package/dist/audioDetection/index.js +1244 -0
  11. package/dist/audioDetection/index.js.map +1 -0
  12. package/dist/audioDetection/index.mjs +1227 -0
  13. package/dist/audioDetection/index.mjs.map +1 -0
  14. package/dist/auth/legacy/core/index.d.mts +42 -0
  15. package/dist/auth/legacy/core/index.d.ts +42 -0
  16. package/dist/auth/legacy/core/index.js +242 -0
  17. package/dist/auth/legacy/core/index.js.map +1 -0
  18. package/dist/auth/legacy/core/index.mjs +226 -0
  19. package/dist/auth/legacy/core/index.mjs.map +1 -0
  20. package/dist/auth/legacy/db/index.d.mts +5 -0
  21. package/dist/auth/legacy/db/index.d.ts +5 -0
  22. package/dist/auth/legacy/db/index.js +261 -0
  23. package/dist/auth/legacy/db/index.js.map +1 -0
  24. package/dist/auth/legacy/db/index.mjs +250 -0
  25. package/dist/auth/legacy/db/index.mjs.map +1 -0
  26. package/dist/auth/legacy/index.d.mts +5 -0
  27. package/dist/auth/legacy/index.d.ts +5 -0
  28. package/dist/auth/legacy/index.js +1107 -0
  29. package/dist/auth/legacy/index.js.map +1 -0
  30. package/dist/auth/legacy/index.mjs +1086 -0
  31. package/dist/auth/legacy/index.mjs.map +1 -0
  32. package/dist/auth/legacy/logic/index.d.mts +9 -0
  33. package/dist/auth/legacy/logic/index.d.ts +9 -0
  34. package/dist/auth/legacy/logic/index.js +194 -0
  35. package/dist/auth/legacy/logic/index.js.map +1 -0
  36. package/dist/auth/legacy/logic/index.mjs +187 -0
  37. package/dist/auth/legacy/logic/index.mjs.map +1 -0
  38. package/dist/auth/legacy/miniapp/index.d.mts +5 -0
  39. package/dist/auth/legacy/miniapp/index.d.ts +5 -0
  40. package/dist/auth/legacy/miniapp/index.js +506 -0
  41. package/dist/auth/legacy/miniapp/index.js.map +1 -0
  42. package/dist/auth/legacy/miniapp/index.mjs +487 -0
  43. package/dist/auth/legacy/miniapp/index.mjs.map +1 -0
  44. package/dist/auth/legacy/routes/index.d.mts +53 -0
  45. package/dist/auth/legacy/routes/index.d.ts +53 -0
  46. package/dist/auth/legacy/routes/index.js +278 -0
  47. package/dist/auth/legacy/routes/index.js.map +1 -0
  48. package/dist/auth/legacy/routes/index.mjs +271 -0
  49. package/dist/auth/legacy/routes/index.mjs.map +1 -0
  50. package/dist/auth/legacy/schema/index.d.mts +401 -0
  51. package/dist/auth/legacy/schema/index.d.ts +401 -0
  52. package/dist/auth/legacy/schema/index.js +50 -0
  53. package/dist/auth/legacy/schema/index.js.map +1 -0
  54. package/dist/auth/legacy/schema/index.mjs +44 -0
  55. package/dist/auth/legacy/schema/index.mjs.map +1 -0
  56. package/dist/auth/legacy/server/index.d.mts +13 -0
  57. package/dist/auth/legacy/server/index.d.ts +13 -0
  58. package/dist/auth/legacy/server/index.js +21 -0
  59. package/dist/auth/legacy/server/index.js.map +1 -0
  60. package/dist/auth/legacy/server/index.mjs +19 -0
  61. package/dist/auth/legacy/server/index.mjs.map +1 -0
  62. package/dist/auth/legacy/services/index.d.mts +40 -0
  63. package/dist/auth/legacy/services/index.d.ts +40 -0
  64. package/dist/auth/legacy/services/index.js +258 -0
  65. package/dist/auth/legacy/services/index.js.map +1 -0
  66. package/dist/auth/legacy/services/index.mjs +252 -0
  67. package/dist/auth/legacy/services/index.mjs.map +1 -0
  68. package/dist/auth/legacy/ui/miniapp/index.d.mts +10 -0
  69. package/dist/auth/legacy/ui/miniapp/index.d.ts +10 -0
  70. package/dist/auth/legacy/ui/miniapp/index.js +298 -0
  71. package/dist/auth/legacy/ui/miniapp/index.js.map +1 -0
  72. package/dist/auth/legacy/ui/miniapp/index.mjs +290 -0
  73. package/dist/auth/legacy/ui/miniapp/index.mjs.map +1 -0
  74. package/dist/auth/legacy/ui/web/index.d.mts +22 -0
  75. package/dist/auth/legacy/ui/web/index.d.ts +22 -0
  76. package/dist/auth/legacy/ui/web/index.js +899 -0
  77. package/dist/auth/legacy/ui/web/index.js.map +1 -0
  78. package/dist/auth/legacy/ui/web/index.mjs +889 -0
  79. package/dist/auth/legacy/ui/web/index.mjs.map +1 -0
  80. package/dist/auth/legacy/web/index.d.mts +5 -0
  81. package/dist/auth/legacy/web/index.d.ts +5 -0
  82. package/dist/auth/legacy/web/index.js +1107 -0
  83. package/dist/auth/legacy/web/index.js.map +1 -0
  84. package/dist/auth/legacy/web/index.mjs +1086 -0
  85. package/dist/auth/legacy/web/index.mjs.map +1 -0
  86. package/dist/auth/rn/index.d.mts +64 -0
  87. package/dist/auth/rn/index.d.ts +64 -0
  88. package/dist/auth/rn/index.js +765 -0
  89. package/dist/auth/rn/index.js.map +1 -0
  90. package/dist/auth/rn/index.mjs +754 -0
  91. package/dist/auth/rn/index.mjs.map +1 -0
  92. package/dist/base-api-client-ACKKt13v.d.mts +277 -0
  93. package/dist/base-api-client-ACKKt13v.d.ts +277 -0
  94. package/dist/boothVaultService-Cn4WPhjg.d.mts +83 -0
  95. package/dist/boothVaultService-Cn4WPhjg.d.ts +83 -0
  96. package/dist/business/index.d.mts +6 -0
  97. package/dist/business/index.d.ts +6 -0
  98. package/dist/business/index.js +1682 -0
  99. package/dist/business/index.js.map +1 -0
  100. package/dist/business/index.mjs +1675 -0
  101. package/dist/business/index.mjs.map +1 -0
  102. package/dist/calendar/index.d.mts +1325 -0
  103. package/dist/calendar/index.d.ts +1325 -0
  104. package/dist/calendar/index.js +5964 -0
  105. package/dist/calendar/index.js.map +1 -0
  106. package/dist/calendar/index.mjs +5878 -0
  107. package/dist/calendar/index.mjs.map +1 -0
  108. package/dist/components/index.d.mts +405 -0
  109. package/dist/components/index.d.ts +405 -0
  110. package/dist/components/index.js +2516 -0
  111. package/dist/components/index.js.map +1 -0
  112. package/dist/components/index.mjs +2396 -0
  113. package/dist/components/index.mjs.map +1 -0
  114. package/dist/drizzle-schema-BNhqj2AZ.d.mts +1114 -0
  115. package/dist/drizzle-schema-BNhqj2AZ.d.ts +1114 -0
  116. package/dist/festivalCard/index.d.mts +75 -0
  117. package/dist/festivalCard/index.d.ts +75 -0
  118. package/dist/festivalCard/index.js +1492 -0
  119. package/dist/festivalCard/index.js.map +1 -0
  120. package/dist/festivalCard/index.mjs +1475 -0
  121. package/dist/festivalCard/index.mjs.map +1 -0
  122. package/dist/festivalCard/server/index.d.mts +120 -0
  123. package/dist/festivalCard/server/index.d.ts +120 -0
  124. package/dist/festivalCard/server/index.js +272 -0
  125. package/dist/festivalCard/server/index.js.map +1 -0
  126. package/dist/festivalCard/server/index.mjs +265 -0
  127. package/dist/festivalCard/server/index.mjs.map +1 -0
  128. package/dist/festivalCardService-CZomuQ4E.d.mts +80 -0
  129. package/dist/festivalCardService-CZomuQ4E.d.ts +80 -0
  130. package/dist/index-1Ag7IBXN.d.ts +144 -0
  131. package/dist/index-DNKZ7-R_.d.mts +184 -0
  132. package/dist/index-DNKZ7-R_.d.ts +184 -0
  133. package/dist/index-DSel44Ke.d.mts +93 -0
  134. package/dist/index-DSel44Ke.d.ts +93 -0
  135. package/dist/index-DdeZSeTJ.d.mts +144 -0
  136. package/dist/index-DrPcMJPc.d.mts +250 -0
  137. package/dist/index-DrPcMJPc.d.ts +250 -0
  138. package/dist/index.d.mts +5333 -0
  139. package/dist/index.d.ts +5333 -0
  140. package/dist/index.js +18809 -0
  141. package/dist/index.js.map +1 -0
  142. package/dist/index.mjs +18533 -0
  143. package/dist/index.mjs.map +1 -0
  144. package/dist/mikuContest/ui/web/index.d.mts +2 -0
  145. package/dist/mikuContest/ui/web/index.d.ts +2 -0
  146. package/dist/mikuContest/ui/web/index.js +353 -0
  147. package/dist/mikuContest/ui/web/index.js.map +1 -0
  148. package/dist/mikuContest/ui/web/index.mjs +343 -0
  149. package/dist/mikuContest/ui/web/index.mjs.map +1 -0
  150. package/dist/mikuFireworks3D/index.d.mts +268 -0
  151. package/dist/mikuFireworks3D/index.d.ts +268 -0
  152. package/dist/mikuFireworks3D/index.js +1267 -0
  153. package/dist/mikuFireworks3D/index.js.map +1 -0
  154. package/dist/mikuFireworks3D/index.mjs +1228 -0
  155. package/dist/mikuFireworks3D/index.mjs.map +1 -0
  156. package/dist/mikuFusionGame/index.d.mts +117 -0
  157. package/dist/mikuFusionGame/index.d.ts +117 -0
  158. package/dist/mikuFusionGame/index.js +1208 -0
  159. package/dist/mikuFusionGame/index.js.map +1 -0
  160. package/dist/mikuFusionGame/index.mjs +1195 -0
  161. package/dist/mikuFusionGame/index.mjs.map +1 -0
  162. package/dist/mmd/admin/index.d.mts +487 -0
  163. package/dist/mmd/admin/index.d.ts +487 -0
  164. package/dist/mmd/admin/index.js +1058 -0
  165. package/dist/mmd/admin/index.js.map +1 -0
  166. package/dist/mmd/admin/index.mjs +1027 -0
  167. package/dist/mmd/admin/index.mjs.map +1 -0
  168. package/dist/mmd/index.d.mts +2467 -0
  169. package/dist/mmd/index.d.ts +2467 -0
  170. package/dist/mmd/index.js +10119 -0
  171. package/dist/mmd/index.js.map +1 -0
  172. package/dist/mmd/index.mjs +10028 -0
  173. package/dist/mmd/index.mjs.map +1 -0
  174. package/dist/mmd/server/index.d.mts +139 -0
  175. package/dist/mmd/server/index.d.ts +139 -0
  176. package/dist/mmd/server/index.js +424 -0
  177. package/dist/mmd/server/index.js.map +1 -0
  178. package/dist/mmd/server/index.mjs +404 -0
  179. package/dist/mmd/server/index.mjs.map +1 -0
  180. package/dist/music/index.d.mts +74 -0
  181. package/dist/music/index.d.ts +74 -0
  182. package/dist/music/index.js +830 -0
  183. package/dist/music/index.js.map +1 -0
  184. package/dist/music/index.mjs +809 -0
  185. package/dist/music/index.mjs.map +1 -0
  186. package/dist/music/server/index.d.mts +1 -0
  187. package/dist/music/server/index.d.ts +1 -0
  188. package/dist/music/server/index.js +194 -0
  189. package/dist/music/server/index.js.map +1 -0
  190. package/dist/music/server/index.mjs +182 -0
  191. package/dist/music/server/index.mjs.map +1 -0
  192. package/dist/navigation/index.d.mts +93 -0
  193. package/dist/navigation/index.d.ts +93 -0
  194. package/dist/navigation/index.js +453 -0
  195. package/dist/navigation/index.js.map +1 -0
  196. package/dist/navigation/index.mjs +443 -0
  197. package/dist/navigation/index.mjs.map +1 -0
  198. package/dist/portfolio/index.d.mts +66 -0
  199. package/dist/portfolio/index.d.ts +66 -0
  200. package/dist/portfolio/index.js +736 -0
  201. package/dist/portfolio/index.js.map +1 -0
  202. package/dist/portfolio/index.mjs +724 -0
  203. package/dist/portfolio/index.mjs.map +1 -0
  204. package/dist/qqbot/server/index.d.mts +216 -0
  205. package/dist/qqbot/server/index.d.ts +216 -0
  206. package/dist/qqbot/server/index.js +394 -0
  207. package/dist/qqbot/server/index.js.map +1 -0
  208. package/dist/qqbot/server/index.mjs +385 -0
  209. package/dist/qqbot/server/index.mjs.map +1 -0
  210. package/dist/qqbot/ui/web/index.d.mts +10 -0
  211. package/dist/qqbot/ui/web/index.d.ts +10 -0
  212. package/dist/qqbot/ui/web/index.js +105 -0
  213. package/dist/qqbot/ui/web/index.js.map +1 -0
  214. package/dist/qqbot/ui/web/index.mjs +99 -0
  215. package/dist/qqbot/ui/web/index.mjs.map +1 -0
  216. package/dist/screenReceiver/index.d.mts +86 -0
  217. package/dist/screenReceiver/index.d.ts +86 -0
  218. package/dist/screenReceiver/index.js +281 -0
  219. package/dist/screenReceiver/index.js.map +1 -0
  220. package/dist/screenReceiver/index.mjs +273 -0
  221. package/dist/screenReceiver/index.mjs.map +1 -0
  222. package/dist/testYourself/admin/index.d.mts +58 -0
  223. package/dist/testYourself/admin/index.d.ts +58 -0
  224. package/dist/testYourself/admin/index.js +1009 -0
  225. package/dist/testYourself/admin/index.js.map +1 -0
  226. package/dist/testYourself/admin/index.mjs +1002 -0
  227. package/dist/testYourself/admin/index.mjs.map +1 -0
  228. package/dist/testYourself/index.d.mts +53 -0
  229. package/dist/testYourself/index.d.ts +53 -0
  230. package/dist/testYourself/index.js +2551 -0
  231. package/dist/testYourself/index.js.map +1 -0
  232. package/dist/testYourself/index.mjs +2531 -0
  233. package/dist/testYourself/index.mjs.map +1 -0
  234. package/dist/testYourself/server/index.d.mts +1029 -0
  235. package/dist/testYourself/server/index.d.ts +1029 -0
  236. package/dist/testYourself/server/index.js +825 -0
  237. package/dist/testYourself/server/index.js.map +1 -0
  238. package/dist/testYourself/server/index.mjs +816 -0
  239. package/dist/testYourself/server/index.mjs.map +1 -0
  240. package/dist/types-BTiaMsBz.d.mts +292 -0
  241. package/dist/types-DyG3ZV9V.d.mts +270 -0
  242. package/dist/types-DyG3ZV9V.d.ts +270 -0
  243. package/dist/types-ERmJyjx8.d.ts +292 -0
  244. package/dist/types-HorDyIRv.d.mts +303 -0
  245. package/dist/types-HorDyIRv.d.ts +303 -0
  246. package/dist/vocaloidBooth/index.d.mts +64 -0
  247. package/dist/vocaloidBooth/index.d.ts +64 -0
  248. package/dist/vocaloidBooth/index.js +376 -0
  249. package/dist/vocaloidBooth/index.js.map +1 -0
  250. package/dist/vocaloidBooth/index.mjs +362 -0
  251. package/dist/vocaloidBooth/index.mjs.map +1 -0
  252. package/dist/vocaloidBooth/server/index.d.mts +111 -0
  253. package/dist/vocaloidBooth/server/index.d.ts +111 -0
  254. package/dist/vocaloidBooth/server/index.js +247 -0
  255. package/dist/vocaloidBooth/server/index.js.map +1 -0
  256. package/dist/vocaloidBooth/server/index.mjs +237 -0
  257. package/dist/vocaloidBooth/server/index.mjs.map +1 -0
  258. package/dist/vocaloidBooth/web/index.d.mts +3 -0
  259. package/dist/vocaloidBooth/web/index.d.ts +3 -0
  260. package/dist/vocaloidBooth/web/index.js +376 -0
  261. package/dist/vocaloidBooth/web/index.js.map +1 -0
  262. package/dist/vocaloidBooth/web/index.mjs +362 -0
  263. package/dist/vocaloidBooth/web/index.mjs.map +1 -0
  264. package/package.json +1 -1
@@ -0,0 +1,2531 @@
1
+ import React2, { useState, useEffect, useRef, useCallback } from 'react';
2
+ import { clsx } from 'clsx';
3
+ import { AlertCircle, X, CheckCircle2, Upload, Plus, RefreshCw, Star, ExternalLink, Edit, Copy, Download, Trash2, Save, Image } from 'lucide-react';
4
+ import { eq, sql, and, or, desc } from 'drizzle-orm';
5
+ import { pgTable, timestamp, varchar, json, uuid, integer, boolean, text, index } from 'drizzle-orm/pg-core';
6
+
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropNames = Object.getOwnPropertyNames;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
16
+
17
+ // src/testYourself/server/ConfigService.ts
18
+ var ConfigService_exports = {};
19
+ __export(ConfigService_exports, {
20
+ ConfigService: () => ConfigService,
21
+ createConfigService: () => createConfigService,
22
+ getDefaultConfigService: () => getDefaultConfigService
23
+ });
24
+ function createConfigService(options) {
25
+ return new ConfigService(options);
26
+ }
27
+ function getDefaultConfigService() {
28
+ if (!defaultServiceInstance) {
29
+ defaultServiceInstance = createConfigService();
30
+ }
31
+ return defaultServiceInstance;
32
+ }
33
+ var LocalStorageAdapter, MemoryStorageAdapter, ConfigService, defaultServiceInstance;
34
+ var init_ConfigService = __esm({
35
+ "src/testYourself/server/ConfigService.ts"() {
36
+ LocalStorageAdapter = class {
37
+ constructor() {
38
+ this.STORAGE_KEY = "test-yourself-configs";
39
+ this.DEFAULT_KEY = "test-yourself-default-config";
40
+ }
41
+ async getAllConfigsData() {
42
+ if (typeof window === "undefined") return [];
43
+ try {
44
+ const data = localStorage.getItem(this.STORAGE_KEY);
45
+ return data ? JSON.parse(data) : [];
46
+ } catch (error) {
47
+ console.error("\u8BFB\u53D6\u914D\u7F6E\u5931\u8D25:", error);
48
+ return [];
49
+ }
50
+ }
51
+ async saveAllConfigsData(configs) {
52
+ if (typeof window === "undefined") return;
53
+ try {
54
+ localStorage.setItem(this.STORAGE_KEY, JSON.stringify(configs));
55
+ } catch (error) {
56
+ console.error("\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25:", error);
57
+ throw error;
58
+ }
59
+ }
60
+ async saveConfig(config) {
61
+ const configs = await this.getAllConfigsData();
62
+ const existingIndex = configs.findIndex((c) => c.id === config.id);
63
+ if (existingIndex >= 0) {
64
+ configs[existingIndex] = config;
65
+ } else {
66
+ configs.push(config);
67
+ }
68
+ await this.saveAllConfigsData(configs);
69
+ }
70
+ async getConfig(id) {
71
+ const configs = await this.getAllConfigsData();
72
+ return configs.find((c) => c.id === id) || null;
73
+ }
74
+ async getAllConfigs() {
75
+ return this.getAllConfigsData();
76
+ }
77
+ async deleteConfig(id) {
78
+ const configs = await this.getAllConfigsData();
79
+ const filtered = configs.filter((c) => c.id !== id);
80
+ await this.saveAllConfigsData(filtered);
81
+ }
82
+ async updateConfig(id, config) {
83
+ const configs = await this.getAllConfigsData();
84
+ const index2 = configs.findIndex((c) => c.id === id);
85
+ if (index2 >= 0) {
86
+ configs[index2] = { ...config, updatedAt: Date.now() };
87
+ await this.saveAllConfigsData(configs);
88
+ } else {
89
+ throw new Error("\u914D\u7F6E\u4E0D\u5B58\u5728: " + id);
90
+ }
91
+ }
92
+ async setDefaultConfig(id) {
93
+ if (typeof window === "undefined") return;
94
+ const config = await this.getConfig(id);
95
+ if (!config) {
96
+ throw new Error("\u914D\u7F6E\u4E0D\u5B58\u5728: " + id);
97
+ }
98
+ const configs = await this.getAllConfigsData();
99
+ configs.forEach((c) => c.isDefault = c.id === id);
100
+ await this.saveAllConfigsData(configs);
101
+ localStorage.setItem(this.DEFAULT_KEY, id);
102
+ }
103
+ async getDefaultConfig() {
104
+ if (typeof window === "undefined") return null;
105
+ try {
106
+ const defaultId = localStorage.getItem(this.DEFAULT_KEY);
107
+ if (defaultId) {
108
+ return await this.getConfig(defaultId);
109
+ }
110
+ const configs = await this.getAllConfigsData();
111
+ return configs.find((c) => c.isDefault) || null;
112
+ } catch (error) {
113
+ console.error("\u83B7\u53D6\u9ED8\u8BA4\u914D\u7F6E\u5931\u8D25:", error);
114
+ return null;
115
+ }
116
+ }
117
+ };
118
+ MemoryStorageAdapter = class {
119
+ constructor() {
120
+ this.configs = /* @__PURE__ */ new Map();
121
+ this.defaultConfigId = null;
122
+ }
123
+ async saveConfig(config) {
124
+ this.configs.set(config.id, config);
125
+ }
126
+ async getConfig(id) {
127
+ return this.configs.get(id) || null;
128
+ }
129
+ async getAllConfigs() {
130
+ return Array.from(this.configs.values());
131
+ }
132
+ async deleteConfig(id) {
133
+ this.configs.delete(id);
134
+ if (this.defaultConfigId === id) {
135
+ this.defaultConfigId = null;
136
+ }
137
+ }
138
+ async updateConfig(id, config) {
139
+ if (!this.configs.has(id)) {
140
+ throw new Error("\u914D\u7F6E\u4E0D\u5B58\u5728: " + id);
141
+ }
142
+ this.configs.set(id, { ...config, updatedAt: Date.now() });
143
+ }
144
+ async setDefaultConfig(id) {
145
+ if (!this.configs.has(id)) {
146
+ throw new Error("\u914D\u7F6E\u4E0D\u5B58\u5728: " + id);
147
+ }
148
+ this.configs.forEach((c) => c.isDefault = c.id === id);
149
+ this.defaultConfigId = id;
150
+ }
151
+ async getDefaultConfig() {
152
+ if (this.defaultConfigId) {
153
+ return this.configs.get(this.defaultConfigId) || null;
154
+ }
155
+ for (const config of this.configs.values()) {
156
+ if (config.isDefault) {
157
+ return config;
158
+ }
159
+ }
160
+ return null;
161
+ }
162
+ };
163
+ ConfigService = class {
164
+ constructor(options = {}) {
165
+ this.cache = /* @__PURE__ */ new Map();
166
+ const {
167
+ storageType = "localStorage",
168
+ customStorage,
169
+ enableCache = true
170
+ } = options;
171
+ this.enableCache = enableCache;
172
+ if (customStorage) {
173
+ this.storage = customStorage;
174
+ } else if (storageType === "memory") {
175
+ this.storage = new MemoryStorageAdapter();
176
+ } else {
177
+ this.storage = new LocalStorageAdapter();
178
+ }
179
+ }
180
+ /**
181
+ * 初始化服务(如果需要)
182
+ */
183
+ async init() {
184
+ }
185
+ /**
186
+ * 生成唯一ID
187
+ */
188
+ generateId() {
189
+ return "config_" + Date.now() + "_" + Math.random().toString(36).substring(2, 9);
190
+ }
191
+ /**
192
+ * 保存配置
193
+ */
194
+ async saveConfig(config) {
195
+ await this.storage.saveConfig(config);
196
+ if (config.isDefault) {
197
+ await this.storage.setDefaultConfig(config.id);
198
+ }
199
+ if (this.enableCache) {
200
+ this.cache.set(config.id, config);
201
+ }
202
+ }
203
+ /**
204
+ * 创建新配置
205
+ */
206
+ async createConfig(name, config, description, isDefault = false) {
207
+ const savedConfig = {
208
+ id: this.generateId(),
209
+ name,
210
+ description,
211
+ config,
212
+ createdAt: Date.now(),
213
+ updatedAt: Date.now(),
214
+ isDefault
215
+ };
216
+ await this.storage.saveConfig(savedConfig);
217
+ if (isDefault) {
218
+ await this.storage.setDefaultConfig(savedConfig.id);
219
+ }
220
+ if (this.enableCache) {
221
+ this.cache.set(savedConfig.id, savedConfig);
222
+ }
223
+ return savedConfig;
224
+ }
225
+ /**
226
+ * 获取配置
227
+ */
228
+ async getConfig(id) {
229
+ if (this.enableCache && this.cache.has(id)) {
230
+ return this.cache.get(id);
231
+ }
232
+ const config = await this.storage.getConfig(id);
233
+ if (config && this.enableCache) {
234
+ this.cache.set(id, config);
235
+ }
236
+ return config;
237
+ }
238
+ /**
239
+ * 获取所有配置列表
240
+ */
241
+ async getAllConfigs() {
242
+ return await this.storage.getAllConfigs();
243
+ }
244
+ /**
245
+ * 获取配置列表(精简版)
246
+ */
247
+ async getConfigList() {
248
+ const configs = await this.getAllConfigs();
249
+ return configs.map((c) => ({
250
+ id: c.id,
251
+ name: c.name,
252
+ description: c.description,
253
+ resultCount: c.config.results.length,
254
+ createdAt: c.createdAt,
255
+ updatedAt: c.updatedAt,
256
+ isDefault: c.isDefault
257
+ }));
258
+ }
259
+ /**
260
+ * 更新配置
261
+ */
262
+ async updateConfig(id, updates) {
263
+ const existing = await this.getConfig(id);
264
+ if (!existing) {
265
+ throw new Error("\u914D\u7F6E\u4E0D\u5B58\u5728: " + id);
266
+ }
267
+ const updated = {
268
+ ...existing,
269
+ ...updates,
270
+ updatedAt: Date.now()
271
+ };
272
+ await this.storage.updateConfig(id, updated);
273
+ if (updates.isDefault) {
274
+ await this.storage.setDefaultConfig(id);
275
+ }
276
+ if (this.enableCache) {
277
+ this.cache.set(id, updated);
278
+ }
279
+ return updated;
280
+ }
281
+ /**
282
+ * 删除配置
283
+ */
284
+ async deleteConfig(id) {
285
+ await this.storage.deleteConfig(id);
286
+ if (this.enableCache) {
287
+ this.cache.delete(id);
288
+ }
289
+ }
290
+ /**
291
+ * 设置默认配置
292
+ */
293
+ async setDefaultConfig(id) {
294
+ await this.storage.setDefaultConfig(id);
295
+ if (this.enableCache) {
296
+ this.cache.clear();
297
+ }
298
+ }
299
+ /**
300
+ * 获取默认配置
301
+ */
302
+ async getDefaultConfig() {
303
+ return await this.storage.getDefaultConfig();
304
+ }
305
+ /**
306
+ * 清空缓存
307
+ */
308
+ clearCache() {
309
+ this.cache.clear();
310
+ }
311
+ /**
312
+ * 导出配置(JSON格式)
313
+ */
314
+ async exportConfig(id) {
315
+ const config = await this.getConfig(id);
316
+ if (!config) {
317
+ throw new Error("\u914D\u7F6E\u4E0D\u5B58\u5728: " + id);
318
+ }
319
+ return JSON.stringify(config, null, 2);
320
+ }
321
+ /**
322
+ * 导入配置
323
+ */
324
+ async importConfig(jsonString) {
325
+ try {
326
+ const data = JSON.parse(jsonString);
327
+ const newConfig = {
328
+ ...data,
329
+ id: this.generateId(),
330
+ createdAt: Date.now(),
331
+ updatedAt: Date.now(),
332
+ isDefault: false
333
+ // 导入的配置不设为默认
334
+ };
335
+ await this.storage.saveConfig(newConfig);
336
+ if (this.enableCache) {
337
+ this.cache.set(newConfig.id, newConfig);
338
+ }
339
+ return newConfig;
340
+ } catch (error) {
341
+ throw new Error("\u5BFC\u5165\u914D\u7F6E\u5931\u8D25: " + error);
342
+ }
343
+ }
344
+ /**
345
+ * 复制配置
346
+ */
347
+ async duplicateConfig(id, newName) {
348
+ const original = await this.getConfig(id);
349
+ if (!original) {
350
+ throw new Error("\u914D\u7F6E\u4E0D\u5B58\u5728: " + id);
351
+ }
352
+ const duplicated = {
353
+ ...original,
354
+ id: this.generateId(),
355
+ name: newName || original.name + " (\u526F\u672C)",
356
+ createdAt: Date.now(),
357
+ updatedAt: Date.now(),
358
+ isDefault: false
359
+ };
360
+ await this.storage.saveConfig(duplicated);
361
+ if (this.enableCache) {
362
+ this.cache.set(duplicated.id, duplicated);
363
+ }
364
+ return duplicated;
365
+ }
366
+ /**
367
+ * 批量删除配置
368
+ */
369
+ async deleteConfigs(ids) {
370
+ for (const id of ids) {
371
+ await this.deleteConfig(id);
372
+ }
373
+ }
374
+ };
375
+ defaultServiceInstance = null;
376
+ }
377
+ });
378
+
379
+ // src/testYourself/utils/fingerprint.ts
380
+ function getCanvasFingerprint() {
381
+ try {
382
+ const canvas = document.createElement("canvas");
383
+ const ctx = canvas.getContext("2d");
384
+ if (!ctx) return "no-canvas";
385
+ canvas.width = 200;
386
+ canvas.height = 50;
387
+ ctx.textBaseline = "top";
388
+ ctx.font = "14px Arial";
389
+ ctx.fillStyle = "#f60";
390
+ ctx.fillRect(0, 0, 200, 50);
391
+ ctx.fillStyle = "#069";
392
+ ctx.fillText("Canvas Fingerprint \u{1F3A8}", 2, 15);
393
+ return canvas.toDataURL();
394
+ } catch (error) {
395
+ return "canvas-error";
396
+ }
397
+ }
398
+ function getWebGLFingerprint() {
399
+ try {
400
+ const canvas = document.createElement("canvas");
401
+ const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
402
+ if (!gl) return "no-webgl";
403
+ const glContext = gl;
404
+ const debugInfo = glContext.getExtension("WEBGL_debug_renderer_info");
405
+ if (debugInfo) {
406
+ const vendor = glContext.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL);
407
+ const renderer = glContext.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
408
+ return vendor + "~" + renderer;
409
+ }
410
+ return "webgl-no-debug";
411
+ } catch (error) {
412
+ return "webgl-error";
413
+ }
414
+ }
415
+ function getAvailableFonts() {
416
+ const testFonts = [
417
+ "Arial",
418
+ "Verdana",
419
+ "Courier New",
420
+ "Georgia",
421
+ "Times New Roman",
422
+ "Comic Sans MS",
423
+ "Trebuchet MS",
424
+ "Arial Black",
425
+ "Impact",
426
+ "Courier",
427
+ "Helvetica",
428
+ "Monaco",
429
+ "Consolas",
430
+ "Menlo"
431
+ ];
432
+ const availableFonts = [];
433
+ const canvas = document.createElement("canvas");
434
+ const ctx = canvas.getContext("2d");
435
+ if (!ctx) return "no-fonts";
436
+ const baseFonts = ["monospace", "sans-serif", "serif"];
437
+ const testString = "mmmmmmmmmmlli";
438
+ const baseWidths = {};
439
+ baseFonts.forEach((font) => {
440
+ ctx.font = "72px " + font;
441
+ baseWidths[font] = ctx.measureText(testString).width;
442
+ });
443
+ testFonts.forEach((font) => {
444
+ let detected = false;
445
+ baseFonts.forEach((baseFont) => {
446
+ ctx.font = "72px " + font + ", " + baseFont;
447
+ const width = ctx.measureText(testString).width;
448
+ if (width !== baseWidths[baseFont]) {
449
+ detected = true;
450
+ }
451
+ });
452
+ if (detected) {
453
+ availableFonts.push(font);
454
+ }
455
+ });
456
+ return availableFonts.join(",") || "no-custom-fonts";
457
+ }
458
+ function getDeviceFingerprint() {
459
+ const fingerprint = {
460
+ // 基础信息
461
+ userAgent: navigator.userAgent,
462
+ screenResolution: window.screen.width + "x" + window.screen.height,
463
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
464
+ language: navigator.language,
465
+ platform: navigator.platform,
466
+ // 显示信息
467
+ colorDepth: window.screen.colorDepth,
468
+ devicePixelRatio: window.devicePixelRatio,
469
+ // 硬件信息
470
+ hardwareConcurrency: navigator.hardwareConcurrency || 0,
471
+ maxTouchPoints: navigator.maxTouchPoints || 0,
472
+ // Canvas和WebGL指纹
473
+ canvasFingerprint: getCanvasFingerprint(),
474
+ webglFingerprint: getWebGLFingerprint(),
475
+ // 字体检测
476
+ fonts: getAvailableFonts(),
477
+ // 浏览器能力
478
+ cookieEnabled: navigator.cookieEnabled,
479
+ localStorageEnabled: (() => {
480
+ try {
481
+ return typeof localStorage !== "undefined";
482
+ } catch {
483
+ return false;
484
+ }
485
+ })(),
486
+ sessionStorageEnabled: (() => {
487
+ try {
488
+ return typeof sessionStorage !== "undefined";
489
+ } catch {
490
+ return false;
491
+ }
492
+ })(),
493
+ indexedDBEnabled: (() => {
494
+ try {
495
+ return typeof indexedDB !== "undefined";
496
+ } catch {
497
+ return false;
498
+ }
499
+ })()
500
+ };
501
+ return fingerprint;
502
+ }
503
+ async function tryGetIPAddress() {
504
+ try {
505
+ const response = await fetch("https://api.ipify.org?format=json", {
506
+ method: "GET",
507
+ mode: "cors"
508
+ });
509
+ if (response.ok) {
510
+ const data = await response.json();
511
+ return data.ip || null;
512
+ }
513
+ } catch (error) {
514
+ console.warn("\u65E0\u6CD5\u83B7\u53D6IP\u5730\u5740:", error);
515
+ }
516
+ return null;
517
+ }
518
+ function simpleHash(str) {
519
+ let hash = 5381;
520
+ for (let i = 0; i < str.length; i++) {
521
+ hash = (hash << 5) + hash + str.charCodeAt(i);
522
+ }
523
+ return Math.abs(hash);
524
+ }
525
+ function generateDeviceHash(fingerprint, salt = "test-yourself-salt-2024") {
526
+ const components = [
527
+ // 基础信息
528
+ fingerprint.userAgent,
529
+ fingerprint.ip || "no-ip",
530
+ fingerprint.screenResolution,
531
+ fingerprint.timezone,
532
+ fingerprint.language,
533
+ fingerprint.platform,
534
+ // 显示信息
535
+ fingerprint.colorDepth?.toString() || "0",
536
+ fingerprint.devicePixelRatio?.toString() || "0",
537
+ // 硬件信息
538
+ fingerprint.hardwareConcurrency?.toString() || "0",
539
+ fingerprint.maxTouchPoints?.toString() || "0",
540
+ // Canvas和WebGL指纹(这些是最独特的)
541
+ fingerprint.canvasFingerprint || "no-canvas",
542
+ fingerprint.webglFingerprint || "no-webgl",
543
+ // 字体(不同设备安装的字体不同)
544
+ fingerprint.fonts || "no-fonts",
545
+ // 浏览器能力
546
+ fingerprint.cookieEnabled ? "1" : "0",
547
+ fingerprint.localStorageEnabled ? "1" : "0",
548
+ fingerprint.sessionStorageEnabled ? "1" : "0",
549
+ fingerprint.indexedDBEnabled ? "1" : "0",
550
+ // 盐值
551
+ salt
552
+ ];
553
+ const combined = components.join("|");
554
+ const hash = simpleHash(combined);
555
+ return hash.toString(36);
556
+ }
557
+ function selectResultIndex(hash, totalResults) {
558
+ const numHash = parseInt(hash, 36);
559
+ return numHash % totalResults;
560
+ }
561
+
562
+ // src/testYourself/data/defaultResults.ts
563
+ var DEFAULT_RESULTS = [
564
+ // 动物系列 (1-15)
565
+ {
566
+ id: "animal-cat",
567
+ title: "\u6175\u61D2\u7684\u732B\u54AA \u{1F431}",
568
+ description: "\u4F60\u5C31\u50CF\u4E00\u53EA\u4F18\u96C5\u7684\u732B\uFF0C\u72EC\u7ACB\u81EA\u4E3B\uFF0C\u559C\u6B22\u6309\u7167\u81EA\u5DF1\u7684\u8282\u594F\u751F\u6D3B\u3002\u6709\u65F6\u9AD8\u51B7\uFF0C\u6709\u65F6\u7C98\u4EBA\uFF0C\u5145\u6EE1\u795E\u79D8\u9B45\u529B\u3002",
569
+ image: "\u{1F431}",
570
+ imageType: "emoji"
571
+ },
572
+ {
573
+ id: "animal-dog",
574
+ title: "\u5FE0\u8BDA\u7684\u72D7\u72D7 \u{1F415}",
575
+ description: "\u4F60\u50CF\u4E00\u53EA\u70ED\u60C5\u7684\u72D7\u72D7\uFF0C\u5BF9\u670B\u53CB\u5FE0\u8BDA\uFF0C\u5145\u6EE1\u6D3B\u529B\u3002\u603B\u662F\u80FD\u7ED9\u5468\u56F4\u7684\u4EBA\u5E26\u6765\u6B22\u4E50\u548C\u6E29\u6696\u3002",
576
+ image: "\u{1F415}",
577
+ imageType: "emoji"
578
+ },
579
+ {
580
+ id: "animal-panda",
581
+ title: "\u53EF\u7231\u7684\u718A\u732B \u{1F43C}",
582
+ description: "\u4F60\u5C31\u50CF\u56FD\u5B9D\u718A\u732B\u4E00\u6837\uFF0C\u5446\u840C\u53EF\u7231\uFF0C\u4EBA\u89C1\u4EBA\u7231\u3002\u6162\u8282\u594F\u7684\u751F\u6D3B\u65B9\u5F0F\u8BA9\u4F60\u5145\u6EE1\u6CBB\u6108\u529B\u3002",
583
+ image: "\u{1F43C}",
584
+ imageType: "emoji"
585
+ },
586
+ {
587
+ id: "animal-fox",
588
+ title: "\u673A\u667A\u7684\u72D0\u72F8 \u{1F98A}",
589
+ description: "\u806A\u660E\u4F36\u4FD0\uFF0C\u53CD\u5E94\u654F\u6377\u3002\u4F60\u603B\u80FD\u7528\u667A\u6167\u89E3\u51B3\u95EE\u9898\uFF0C\u662F\u56E2\u961F\u4E2D\u7684\u667A\u56CA\u62C5\u5F53\u3002",
590
+ image: "\u{1F98A}",
591
+ imageType: "emoji"
592
+ },
593
+ {
594
+ id: "animal-owl",
595
+ title: "\u777F\u667A\u7684\u732B\u5934\u9E70 \u{1F989}",
596
+ description: "\u4F60\u50CF\u732B\u5934\u9E70\u4E00\u6837\u5145\u6EE1\u667A\u6167\uFF0C\u559C\u6B22\u5728\u591C\u6DF1\u4EBA\u9759\u65F6\u601D\u8003\u3002\u7406\u6027\u3001\u6C89\u7A33\u662F\u4F60\u7684\u6807\u7B7E\u3002",
597
+ image: "\u{1F989}",
598
+ imageType: "emoji"
599
+ },
600
+ {
601
+ id: "animal-dolphin",
602
+ title: "\u5FEB\u4E50\u7684\u6D77\u8C5A \u{1F42C}",
603
+ description: "\u6D3B\u6CFC\u5F00\u6717\uFF0C\u793E\u4EA4\u8FBE\u4EBA\u3002\u4F60\u7684\u7B11\u5BB9\u80FD\u591F\u611F\u67D3\u8EAB\u8FB9\u7684\u6BCF\u4E00\u4E2A\u4EBA\u3002",
604
+ image: "\u{1F42C}",
605
+ imageType: "emoji"
606
+ },
607
+ {
608
+ id: "animal-butterfly",
609
+ title: "\u81EA\u7531\u7684\u8774\u8776 \u{1F98B}",
610
+ description: "\u8FFD\u6C42\u81EA\u7531\uFF0C\u70ED\u7231\u7F8E\u597D\u4E8B\u7269\u3002\u4F60\u7684\u4EBA\u751F\u5145\u6EE1\u8272\u5F69\uFF0C\u4ECE\u4E0D\u88AB\u675F\u7F1A\u3002",
611
+ image: "\u{1F98B}",
612
+ imageType: "emoji"
613
+ },
614
+ {
615
+ id: "animal-lion",
616
+ title: "\u52C7\u6562\u7684\u72EE\u5B50 \u{1F981}",
617
+ description: "\u5929\u751F\u7684\u9886\u5BFC\u8005\uFF0C\u52C7\u6562\u679C\u65AD\u3002\u4F60\u7684\u6C14\u573A\u5F3A\u5927\uFF0C\u603B\u80FD\u6FC0\u52B1\u4ED6\u4EBA\u3002",
618
+ image: "\u{1F981}",
619
+ imageType: "emoji"
620
+ },
621
+ {
622
+ id: "animal-rabbit",
623
+ title: "\u6E29\u67D4\u7684\u5154\u5B50 \u{1F430}",
624
+ description: "\u5FC3\u5730\u5584\u826F\uFF0C\u6E29\u67D4\u4F53\u8D34\u3002\u4F60\u7684\u5B58\u5728\u5C31\u50CF\u6625\u5929\u7684\u5FAE\u98CE\uFF0C\u8BA9\u4EBA\u611F\u5230\u6E29\u6696\u3002",
625
+ image: "\u{1F430}",
626
+ imageType: "emoji"
627
+ },
628
+ {
629
+ id: "animal-penguin",
630
+ title: "\u5446\u840C\u7684\u4F01\u9E45 \u{1F427}",
631
+ description: "\u61A8\u6001\u53EF\u63AC\uFF0C\u8BA4\u771F\u6267\u7740\u3002\u867D\u7136\u770B\u8D77\u6765\u7B28\u62D9\uFF0C\u4F46\u505A\u4E8B\u4E00\u4E1D\u4E0D\u82DF\u3002",
632
+ image: "\u{1F427}",
633
+ imageType: "emoji"
634
+ },
635
+ {
636
+ id: "animal-eagle",
637
+ title: "\u7FF1\u7FD4\u7684\u96C4\u9E70 \u{1F985}",
638
+ description: "\u76EE\u5149\u8FDC\u5927\uFF0C\u5FD7\u5411\u9AD8\u8FDC\u3002\u4F60\u603B\u662F\u80FD\u770B\u5230\u522B\u4EBA\u770B\u4E0D\u5230\u7684\u673A\u4F1A\u3002",
639
+ image: "\u{1F985}",
640
+ imageType: "emoji"
641
+ },
642
+ {
643
+ id: "animal-koala",
644
+ title: "\u6175\u61D2\u7684\u8003\u62C9 \u{1F428}",
645
+ description: "\u4F5B\u7CFB\u751F\u6D3B\uFF0C\u77E5\u8DB3\u5E38\u4E50\u3002\u4F60\u61C2\u5F97\u4EAB\u53D7\u5F53\u4E0B\uFF0C\u6D3B\u5728\u81EA\u5DF1\u7684\u8282\u594F\u91CC\u3002",
646
+ image: "\u{1F428}",
647
+ imageType: "emoji"
648
+ },
649
+ {
650
+ id: "animal-sloth",
651
+ title: "\u60A0\u95F2\u7684\u6811\u61D2 \u{1F9A5}",
652
+ description: "\u6162\u6162\u6765\uFF0C\u6BD4\u8F83\u5FEB\u3002\u4F60\u76F8\u4FE1\u6025\u4E0D\u6765\u7684\u4E8B\u5C31\u4E0D\u8981\u6025\uFF0C\u4FDD\u6301\u81EA\u5DF1\u7684pace\u3002",
653
+ image: "\u{1F9A5}",
654
+ imageType: "emoji"
655
+ },
656
+ {
657
+ id: "animal-unicorn",
658
+ title: "\u68A6\u5E7B\u7684\u72EC\u89D2\u517D \u{1F984}",
659
+ description: "\u5145\u6EE1\u5E7B\u60F3\uFF0C\u8FFD\u6C42\u5B8C\u7F8E\u3002\u4F60\u7684\u4E16\u754C\u4E94\u5F69\u6591\u6593\uFF0C\u603B\u6709\u72EC\u7279\u7684\u60F3\u6CD5\u3002",
660
+ image: "\u{1F984}",
661
+ imageType: "emoji"
662
+ },
663
+ {
664
+ id: "animal-dragon",
665
+ title: "\u795E\u79D8\u7684\u9F99 \u{1F409}",
666
+ description: "\u5F3A\u5927\u800C\u795E\u79D8\uFF0C\u5145\u6EE1\u9B45\u529B\u3002\u4F60\u662F\u4F20\u8BF4\u4E2D\u7684\u5B58\u5728\uFF0C\u4EE4\u4EBA\u5411\u5F80\u3002",
667
+ image: "\u{1F409}",
668
+ imageType: "emoji"
669
+ },
670
+ // 星球系列 (16-25)
671
+ {
672
+ id: "planet-sun",
673
+ title: "\u6E29\u6696\u7684\u592A\u9633 \u2600\uFE0F",
674
+ description: "\u4F60\u5C31\u50CF\u592A\u9633\u4E00\u6837\uFF0C\u662F\u56E2\u961F\u7684\u80FD\u91CF\u6765\u6E90\uFF0C\u6E29\u6696\u7740\u5468\u56F4\u7684\u6BCF\u4E2A\u4EBA\u3002",
675
+ image: "\u2600\uFE0F",
676
+ imageType: "emoji"
677
+ },
678
+ {
679
+ id: "planet-moon",
680
+ title: "\u6E29\u67D4\u7684\u6708\u4EAE \u{1F319}",
681
+ description: "\u5B89\u9759\u800C\u6E29\u67D4\uFF0C\u5728\u9ED1\u6697\u4E2D\u7ED9\u4EBA\u5149\u660E\u3002\u4F60\u662F\u591C\u665A\u6700\u7F8E\u7684\u966A\u4F34\u3002",
682
+ image: "\u{1F319}",
683
+ imageType: "emoji"
684
+ },
685
+ {
686
+ id: "planet-star",
687
+ title: "\u95EA\u8000\u7684\u661F\u661F \u2B50",
688
+ description: "\u867D\u7136\u6E3A\u5C0F\uFF0C\u4F46\u59CB\u7EC8\u95EA\u8000\u3002\u4F60\u7528\u81EA\u5DF1\u7684\u65B9\u5F0F\u53D1\u5149\u53D1\u70ED\u3002",
689
+ image: "\u2B50",
690
+ imageType: "emoji"
691
+ },
692
+ {
693
+ id: "planet-earth",
694
+ title: "\u5305\u5BB9\u7684\u5730\u7403 \u{1F30D}",
695
+ description: "\u5305\u5BB9\u4E07\u7269\uFF0C\u751F\u673A\u52C3\u52C3\u3002\u4F60\u6709\u7740\u5BBD\u5E7F\u7684\u80F8\u6000\u548C\u65E0\u9650\u7684\u53EF\u80FD\u3002",
696
+ image: "\u{1F30D}",
697
+ imageType: "emoji"
698
+ },
699
+ {
700
+ id: "planet-saturn",
701
+ title: "\u72EC\u7279\u7684\u571F\u661F \u{1FA90}",
702
+ description: "\u4E0E\u4F17\u4E0D\u540C\uFF0C\u81EA\u6210\u4E00\u683C\u3002\u4F60\u7684\u4E2A\u6027\u5C31\u50CF\u571F\u661F\u73AF\u4E00\u6837\u4EE4\u4EBA\u7740\u8FF7\u3002",
703
+ image: "\u{1FA90}",
704
+ imageType: "emoji"
705
+ },
706
+ {
707
+ id: "weather-rainbow",
708
+ title: "\u7EDA\u4E3D\u7684\u5F69\u8679 \u{1F308}",
709
+ description: "\u98CE\u96E8\u8FC7\u540E\u89C1\u5F69\u8679\uFF0C\u4F60\u603B\u80FD\u5728\u56F0\u96BE\u540E\u770B\u5230\u5E0C\u671B\u548C\u7F8E\u597D\u3002",
710
+ image: "\u{1F308}",
711
+ imageType: "emoji"
712
+ },
713
+ {
714
+ id: "weather-cloud",
715
+ title: "\u98D8\u9038\u7684\u4E91\u6735 \u2601\uFE0F",
716
+ description: "\u81EA\u7531\u81EA\u5728\uFF0C\u968F\u98CE\u98D8\u8361\u3002\u4F60\u4E0D\u88AB\u5B9A\u4E49\uFF0C\u6C38\u8FDC\u5145\u6EE1\u53EF\u80FD\u3002",
717
+ image: "\u2601\uFE0F",
718
+ imageType: "emoji"
719
+ },
720
+ {
721
+ id: "weather-lightning",
722
+ title: "\u95EA\u7535 \u26A1",
723
+ description: "\u7206\u53D1\u529B\u5F3A\uFF0C\u884C\u52A8\u8FC5\u901F\u3002\u4F60\u7684\u80FD\u91CF\u5C31\u50CF\u95EA\u7535\u4E00\u6837\u4EE4\u4EBA\u9707\u64BC\u3002",
724
+ image: "\u26A1",
725
+ imageType: "emoji"
726
+ },
727
+ {
728
+ id: "weather-snowflake",
729
+ title: "\u72EC\u7279\u7684\u96EA\u82B1 \u2744\uFE0F",
730
+ description: "\u4E16\u754C\u4E0A\u6CA1\u6709\u4E24\u7247\u76F8\u540C\u7684\u96EA\u82B1\uFF0C\u4F60\u4E5F\u662F\u72EC\u4E00\u65E0\u4E8C\u7684\u5B58\u5728\u3002",
731
+ image: "\u2744\uFE0F",
732
+ imageType: "emoji"
733
+ },
734
+ {
735
+ id: "weather-fire",
736
+ title: "\u70ED\u60C5\u7684\u706B\u7130 \u{1F525}",
737
+ description: "\u70ED\u60C5\u4F3C\u706B\uFF0C\u5145\u6EE1\u6FC0\u60C5\u3002\u4F60\u7684\u5B58\u5728\u80FD\u70B9\u71C3\u5468\u56F4\u7684\u6C14\u6C1B\u3002",
738
+ image: "\u{1F525}",
739
+ imageType: "emoji"
740
+ },
741
+ // 植物系列 (26-35)
742
+ {
743
+ id: "plant-tree",
744
+ title: "\u575A\u97E7\u7684\u5927\u6811 \u{1F333}",
745
+ description: "\u7A33\u91CD\u53EF\u9760\uFF0C\u6839\u57FA\u6DF1\u539A\u3002\u4F60\u662F\u5927\u5BB6\u53EF\u4EE5\u4F9D\u9760\u7684\u5B58\u5728\u3002",
746
+ image: "\u{1F333}",
747
+ imageType: "emoji"
748
+ },
749
+ {
750
+ id: "plant-flower",
751
+ title: "\u7F8E\u4E3D\u7684\u82B1\u6735 \u{1F338}",
752
+ description: "\u7EFD\u653E\u81EA\u6211\uFF0C\u7F8E\u4E3D\u52A8\u4EBA\u3002\u4F60\u7684\u5B58\u5728\u5C31\u662F\u4E00\u9053\u9753\u4E3D\u7684\u98CE\u666F\u3002",
753
+ image: "\u{1F338}",
754
+ imageType: "emoji"
755
+ },
756
+ {
757
+ id: "plant-sunflower",
758
+ title: "\u5411\u9633\u7684\u5411\u65E5\u8475 \u{1F33B}",
759
+ description: "\u6C38\u8FDC\u5411\u7740\u9633\u5149\uFF0C\u79EF\u6781\u5411\u4E0A\u3002\u4F60\u7684\u4E50\u89C2\u611F\u67D3\u7740\u6BCF\u4E2A\u4EBA\u3002",
760
+ image: "\u{1F33B}",
761
+ imageType: "emoji"
762
+ },
763
+ {
764
+ id: "plant-rose",
765
+ title: "\u4F18\u96C5\u7684\u73AB\u7470 \u{1F339}",
766
+ description: "\u9AD8\u8D35\u4F18\u96C5\uFF0C\u5145\u6EE1\u9B45\u529B\u3002\u867D\u6709\u523A\u4F46\u66F4\u6709\u7F8E\u4E3D\u3002",
767
+ image: "\u{1F339}",
768
+ imageType: "emoji"
769
+ },
770
+ {
771
+ id: "plant-cactus",
772
+ title: "\u575A\u5F3A\u7684\u4ED9\u4EBA\u638C \u{1F335}",
773
+ description: "\u5728\u8270\u96BE\u73AF\u5883\u4E2D\u4F9D\u7136\u575A\u5F3A\u3002\u4F60\u7684\u97E7\u6027\u4EE4\u4EBA\u656C\u4F69\u3002",
774
+ image: "\u{1F335}",
775
+ imageType: "emoji"
776
+ },
777
+ {
778
+ id: "plant-clover",
779
+ title: "\u5E78\u8FD0\u7684\u56DB\u53F6\u8349 \u{1F340}",
780
+ description: "\u5E78\u8FD0\u7684\u8C61\u5F81\uFF0C\u603B\u80FD\u7ED9\u4EBA\u5E26\u6765\u597D\u8FD0\u548C\u5E0C\u671B\u3002",
781
+ image: "\u{1F340}",
782
+ imageType: "emoji"
783
+ },
784
+ {
785
+ id: "plant-maple",
786
+ title: "\u6D6A\u6F2B\u7684\u67AB\u53F6 \u{1F341}",
787
+ description: "\u6D6A\u6F2B\u800C\u8BD7\u610F\uFF0C\u4F60\u7684\u4E16\u754C\u5145\u6EE1\u827A\u672F\u6C14\u606F\u3002",
788
+ image: "\u{1F341}",
789
+ imageType: "emoji"
790
+ },
791
+ {
792
+ id: "plant-mushroom",
793
+ title: "\u795E\u79D8\u7684\u8611\u83C7 \u{1F344}",
794
+ description: "\u4F4E\u8C03\u795E\u79D8\uFF0C\u603B\u6709\u610F\u60F3\u4E0D\u5230\u7684\u60CA\u559C\u3002",
795
+ image: "\u{1F344}",
796
+ imageType: "emoji"
797
+ },
798
+ {
799
+ id: "plant-cherry",
800
+ title: "\u6D6A\u6F2B\u7684\u6A31\u82B1 \u{1F338}",
801
+ description: "\u77ED\u6682\u800C\u7F8E\u597D\uFF0C\u4F60\u73CD\u60DC\u6BCF\u4E00\u4E2A\u77AC\u95F4\u7684\u7F8E\u4E3D\u3002",
802
+ image: "\u{1F338}",
803
+ imageType: "emoji"
804
+ },
805
+ {
806
+ id: "plant-bamboo",
807
+ title: "\u575A\u97E7\u7684\u7AF9\u5B50 \u{1F38B}",
808
+ description: "\u865A\u5FC3\u6709\u8282\uFF0C\u6108\u632B\u6108\u52C7\u3002\u4F60\u7684\u7CBE\u795E\u503C\u5F97\u5B66\u4E60\u3002",
809
+ image: "\u{1F38B}",
810
+ imageType: "emoji"
811
+ },
812
+ // 食物系列 (36-45)
813
+ {
814
+ id: "food-coffee",
815
+ title: "\u63D0\u795E\u7684\u5496\u5561 \u2615",
816
+ description: "\u4F60\u5C31\u50CF\u5496\u5561\uFF0C\u662F\u5927\u5BB6\u7684\u80FD\u91CF\u6765\u6E90\uFF0C\u5E2E\u52A9\u4ED6\u4EBA\u4FDD\u6301\u6D3B\u529B\u3002",
817
+ image: "\u2615",
818
+ imageType: "emoji"
819
+ },
820
+ {
821
+ id: "food-pizza",
822
+ title: "\u5FEB\u4E50\u7684\u62AB\u8428 \u{1F355}",
823
+ description: "\u4EBA\u89C1\u4EBA\u7231\uFF0C\u603B\u80FD\u5E26\u6765\u5FEB\u4E50\u3002\u4F60\u662F\u805A\u4F1A\u7684\u7075\u9B42\u3002",
824
+ image: "\u{1F355}",
825
+ imageType: "emoji"
826
+ },
827
+ {
828
+ id: "food-cookie",
829
+ title: "\u751C\u871C\u7684\u997C\u5E72 \u{1F36A}",
830
+ description: "\u6E29\u6696\u751C\u871C\uFF0C\u7ED9\u4EBAcomfort\u3002\u4F60\u662F\u6700\u597D\u7684\u6CBB\u6108\u7CFB\u5B58\u5728\u3002",
831
+ image: "\u{1F36A}",
832
+ imageType: "emoji"
833
+ },
834
+ {
835
+ id: "food-icecream",
836
+ title: "\u6E05\u51C9\u7684\u51B0\u6DC7\u6DCB \u{1F366}",
837
+ description: "\u751C\u7F8E\u53EF\u7231\uFF0C\u5728\u708E\u70ED\u65F6\u7ED9\u4EBA\u6E05\u51C9\u3002\u4F60\u603B\u80FD\u5728\u5173\u952E\u65F6\u523B\u51FA\u73B0\u3002",
838
+ image: "\u{1F366}",
839
+ imageType: "emoji"
840
+ },
841
+ {
842
+ id: "food-honey",
843
+ title: "\u751C\u871C\u7684\u8702\u871C \u{1F36F}",
844
+ description: "\u5929\u7136\u751C\u7F8E\uFF0C\u7EAF\u7CB9\u53EF\u7231\u3002\u4F60\u7684\u5B58\u5728\u5C31\u662F\u5E78\u798F\u7684\u5473\u9053\u3002",
845
+ image: "\u{1F36F}",
846
+ imageType: "emoji"
847
+ },
848
+ {
849
+ id: "food-sushi",
850
+ title: "\u7CBE\u81F4\u7684\u5BFF\u53F8 \u{1F363}",
851
+ description: "\u7CBE\u81F4\u4F18\u96C5\uFF0C\u6CE8\u91CD\u7EC6\u8282\u3002\u4F60\u5BF9\u751F\u6D3B\u6709\u7740\u72EC\u7279\u7684\u54C1\u5473\u3002",
852
+ image: "\u{1F363}",
853
+ imageType: "emoji"
854
+ },
855
+ {
856
+ id: "food-cake",
857
+ title: "\u751C\u7F8E\u7684\u86CB\u7CD5 \u{1F382}",
858
+ description: "\u751C\u871C\u7F8E\u597D\uFF0C\u662F\u5E86\u795D\u7684\u8C61\u5F81\u3002\u4F60\u603B\u80FD\u7ED9\u4EBA\u5E26\u6765\u60CA\u559C\u3002",
859
+ image: "\u{1F382}",
860
+ imageType: "emoji"
861
+ },
862
+ {
863
+ id: "food-donut",
864
+ title: "\u53EF\u7231\u7684\u751C\u751C\u5708 \u{1F369}",
865
+ description: "\u5706\u5706\u6EE1\u6EE1\uFF0C\u751C\u871C\u53EF\u7231\u3002\u4F60\u7684\u5B58\u5728\u8BA9\u751F\u6D3B\u66F4\u7F8E\u597D\u3002",
866
+ image: "\u{1F369}",
867
+ imageType: "emoji"
868
+ },
869
+ {
870
+ id: "food-lollipop",
871
+ title: "\u7AE5\u771F\u7684\u68D2\u68D2\u7CD6 \u{1F36D}",
872
+ description: "\u4FDD\u6301\u7AE5\u5FC3\uFF0C\u6C38\u8FDC\u5E74\u8F7B\u3002\u4F60\u7684\u7EAF\u771F\u4EE4\u4EBA\u7FA1\u6155\u3002",
873
+ image: "\u{1F36D}",
874
+ imageType: "emoji"
875
+ },
876
+ {
877
+ id: "food-watermelon",
878
+ title: "\u6E05\u723D\u7684\u897F\u74DC \u{1F349}",
879
+ description: "\u6E05\u723D\u89E3\u6E34\uFF0C\u590F\u65E5\u5FC5\u5907\u3002\u4F60\u603B\u80FD\u5E26\u6765\u6E05\u65B0\u7684\u611F\u89C9\u3002",
880
+ image: "\u{1F349}",
881
+ imageType: "emoji"
882
+ }
883
+ ];
884
+ if (DEFAULT_RESULTS.length !== 45) {
885
+ console.warn("\u7ED3\u679C\u6570\u636E\u96C6\u5E94\u5305\u542B45\u9879\uFF0C\u5F53\u524D: " + DEFAULT_RESULTS.length);
886
+ }
887
+
888
+ // src/testYourself/components/TestYourself.tsx
889
+ var STORAGE_KEY = "test-yourself-result";
890
+ var TestYourself = ({
891
+ config: propConfig,
892
+ configId,
893
+ onResult,
894
+ className = ""
895
+ }) => {
896
+ const [loadedConfig, setLoadedConfig] = useState(null);
897
+ const [configLoading, setConfigLoading] = useState(false);
898
+ useEffect(() => {
899
+ const loadConfig = async () => {
900
+ if (configId) {
901
+ setConfigLoading(true);
902
+ try {
903
+ const { getDefaultConfigService: getDefaultConfigService2 } = await Promise.resolve().then(() => (init_ConfigService(), ConfigService_exports));
904
+ const service = getDefaultConfigService2();
905
+ const savedConfig = await service.getConfig(configId);
906
+ if (savedConfig) {
907
+ setLoadedConfig(savedConfig.config);
908
+ } else {
909
+ console.warn("\u914D\u7F6E " + configId + " \u4E0D\u5B58\u5728\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E");
910
+ }
911
+ } catch (error) {
912
+ console.error("\u52A0\u8F7D\u914D\u7F6E\u5931\u8D25:", error);
913
+ } finally {
914
+ setConfigLoading(false);
915
+ }
916
+ }
917
+ };
918
+ loadConfig();
919
+ }, [configId]);
920
+ const config = loadedConfig || propConfig || {
921
+ gameTitle: "\u6D4B\u6D4B\u4F60\u662F\u4EC0\u4E48",
922
+ gameDescription: "\u957F\u6309\u6309\u94AE\uFF0C\u53D1\u73B0\u4F60\u7684\u4E13\u5C5E\u5C5E\u6027",
923
+ results: DEFAULT_RESULTS
924
+ };
925
+ const {
926
+ gameTitle,
927
+ gameDescription,
928
+ buttonText = "\u957F\u6309\u5F00\u59CB\u6D4B\u8BD5",
929
+ longPressDuration = 2e3,
930
+ results = DEFAULT_RESULTS,
931
+ enableIPFetch = false,
932
+ customSalt,
933
+ resultStyle = "card"
934
+ } = config;
935
+ const [status, setStatus] = useState("idle");
936
+ const [result, setResult] = useState(null);
937
+ const [pressProgress, setPressProgress] = useState(0);
938
+ const [ipWarning, setIpWarning] = useState(null);
939
+ const [isLoading, setIsLoading] = useState(true);
940
+ const pressTimerRef = useRef(null);
941
+ const progressIntervalRef = useRef(null);
942
+ const startTimeRef = useRef(0);
943
+ useEffect(() => {
944
+ const initializeTest = async () => {
945
+ if (configLoading) {
946
+ return;
947
+ }
948
+ const storageKey = configId ? STORAGE_KEY + "_" + configId : STORAGE_KEY;
949
+ const savedResult = localStorage.getItem(storageKey);
950
+ if (savedResult) {
951
+ try {
952
+ const parsed = JSON.parse(savedResult);
953
+ setResult(parsed);
954
+ setStatus("completed");
955
+ setIsLoading(false);
956
+ return;
957
+ } catch (error) {
958
+ console.error("\u89E3\u6790\u4FDD\u5B58\u7684\u7ED3\u679C\u5931\u8D25:", error);
959
+ }
960
+ }
961
+ if (enableIPFetch) {
962
+ const ip = await tryGetIPAddress();
963
+ if (!ip) {
964
+ setIpWarning("\u26A0\uFE0F \u65E0\u6CD5\u83B7\u53D6IP\u5730\u5740\uFF0C\u5C06\u4EC5\u4F7F\u7528\u6D4F\u89C8\u5668\u6307\u7EB9\u751F\u6210\u7ED3\u679C");
965
+ }
966
+ }
967
+ setIsLoading(false);
968
+ };
969
+ initializeTest();
970
+ }, [enableIPFetch, configLoading, configId]);
971
+ const calculateResult = async () => {
972
+ try {
973
+ const fingerprint = getDeviceFingerprint();
974
+ if (enableIPFetch) {
975
+ const ip = await tryGetIPAddress();
976
+ if (ip) {
977
+ fingerprint.ip = ip;
978
+ }
979
+ }
980
+ const actualResults = results.length > 0 ? results : DEFAULT_RESULTS;
981
+ const hash = generateDeviceHash(fingerprint, customSalt);
982
+ const index2 = selectResultIndex(hash, actualResults.length);
983
+ const selectedResult = actualResults[index2];
984
+ if (!selectedResult) {
985
+ console.error("\u65E0\u6CD5\u83B7\u53D6\u6D4B\u8BD5\u7ED3\u679C\uFF0Cindex:", index2, "total:", actualResults.length);
986
+ throw new Error("\u65E0\u6CD5\u83B7\u53D6\u6D4B\u8BD5\u7ED3\u679C");
987
+ }
988
+ console.log("\u8BA1\u7B97\u7ED3\u679C\u6210\u529F:", selectedResult);
989
+ const storageKey = configId ? STORAGE_KEY + "_" + configId : STORAGE_KEY;
990
+ localStorage.setItem(storageKey, JSON.stringify(selectedResult));
991
+ return selectedResult;
992
+ } catch (error) {
993
+ console.error("\u8BA1\u7B97\u7ED3\u679C\u5931\u8D25:", error);
994
+ throw error;
995
+ }
996
+ };
997
+ const handlePressStart = (e) => {
998
+ if (status !== "idle") return;
999
+ e.preventDefault();
1000
+ setStatus("pressing");
1001
+ startTimeRef.current = Date.now();
1002
+ progressIntervalRef.current = setInterval(() => {
1003
+ const elapsed = Date.now() - startTimeRef.current;
1004
+ const progress = Math.min(elapsed / longPressDuration * 100, 100);
1005
+ setPressProgress(progress);
1006
+ }, 16);
1007
+ pressTimerRef.current = setTimeout(async () => {
1008
+ try {
1009
+ setPressProgress(100);
1010
+ if (progressIntervalRef.current) {
1011
+ clearInterval(progressIntervalRef.current);
1012
+ progressIntervalRef.current = null;
1013
+ }
1014
+ console.log("\u5F00\u59CB\u8BA1\u7B97\u7ED3\u679C...");
1015
+ const testResult = await calculateResult();
1016
+ console.log("\u7ED3\u679C\u8BA1\u7B97\u5B8C\u6210\uFF0C\u66F4\u65B0\u72B6\u6001:", testResult);
1017
+ setResult(testResult);
1018
+ setTimeout(() => {
1019
+ setStatus("completed");
1020
+ console.log("\u72B6\u6001\u5DF2\u66F4\u65B0\u4E3A completed");
1021
+ }, 0);
1022
+ if (onResult) {
1023
+ onResult(testResult);
1024
+ }
1025
+ } catch (error) {
1026
+ console.error("\u6D4B\u8BD5\u5931\u8D25:", error);
1027
+ setStatus("idle");
1028
+ setPressProgress(0);
1029
+ alert("\u6D4B\u8BD5\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
1030
+ }
1031
+ }, longPressDuration);
1032
+ if ("button" in e && e.button === 0) {
1033
+ const handleGlobalMouseUp = () => {
1034
+ handlePressEnd();
1035
+ document.removeEventListener("mouseup", handleGlobalMouseUp);
1036
+ };
1037
+ document.addEventListener("mouseup", handleGlobalMouseUp);
1038
+ }
1039
+ };
1040
+ const handlePressEnd = () => {
1041
+ if (status !== "pressing") return;
1042
+ if (pressTimerRef.current) {
1043
+ clearTimeout(pressTimerRef.current);
1044
+ pressTimerRef.current = null;
1045
+ }
1046
+ if (progressIntervalRef.current) {
1047
+ clearInterval(progressIntervalRef.current);
1048
+ progressIntervalRef.current = null;
1049
+ }
1050
+ setStatus("idle");
1051
+ setPressProgress(0);
1052
+ };
1053
+ const handleMouseLeave = (e) => {
1054
+ };
1055
+ const handleTouchMove = (e) => {
1056
+ const touch = e.touches[0];
1057
+ if (!touch) return;
1058
+ const target = e.currentTarget;
1059
+ const rect = target.getBoundingClientRect();
1060
+ const isInside = touch.clientX >= rect.left && touch.clientX <= rect.right && touch.clientY >= rect.top && touch.clientY <= rect.bottom;
1061
+ if (!isInside && status === "pressing") {
1062
+ handlePressEnd();
1063
+ }
1064
+ };
1065
+ useEffect(() => {
1066
+ return () => {
1067
+ if (pressTimerRef.current) {
1068
+ clearTimeout(pressTimerRef.current);
1069
+ }
1070
+ if (progressIntervalRef.current) {
1071
+ clearInterval(progressIntervalRef.current);
1072
+ }
1073
+ };
1074
+ }, []);
1075
+ const handleReset = () => {
1076
+ const storageKey = configId ? STORAGE_KEY + "_" + configId : STORAGE_KEY;
1077
+ localStorage.removeItem(storageKey);
1078
+ setResult(null);
1079
+ setStatus("idle");
1080
+ setPressProgress(0);
1081
+ };
1082
+ const backgroundStyle = {
1083
+ position: "relative",
1084
+ minHeight: "100vh",
1085
+ overflow: "hidden",
1086
+ background: "linear-gradient(135deg, #f3e8ff 0%, #fce7f3 50%, #dbeafe 100%)"
1087
+ };
1088
+ const DecorativeBackground = () => /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("div", { style: {
1089
+ position: "absolute",
1090
+ top: 0,
1091
+ left: 0,
1092
+ width: "384px",
1093
+ height: "384px",
1094
+ background: "radial-gradient(circle, rgba(192, 132, 252, 0.3) 0%, transparent 70%)",
1095
+ borderRadius: "50%",
1096
+ filter: "blur(60px)",
1097
+ transform: "translate(-50%, -50%)",
1098
+ pointerEvents: "none"
1099
+ } }), /* @__PURE__ */ React2.createElement("div", { style: {
1100
+ position: "absolute",
1101
+ top: "50%",
1102
+ right: 0,
1103
+ width: "384px",
1104
+ height: "384px",
1105
+ background: "radial-gradient(circle, rgba(244, 114, 182, 0.3) 0%, transparent 70%)",
1106
+ borderRadius: "50%",
1107
+ filter: "blur(60px)",
1108
+ transform: "translateX(50%)",
1109
+ pointerEvents: "none"
1110
+ } }), /* @__PURE__ */ React2.createElement("div", { style: {
1111
+ position: "absolute",
1112
+ bottom: 0,
1113
+ left: "50%",
1114
+ width: "384px",
1115
+ height: "384px",
1116
+ background: "radial-gradient(circle, rgba(147, 197, 253, 0.3) 0%, transparent 70%)",
1117
+ borderRadius: "50%",
1118
+ filter: "blur(60px)",
1119
+ transform: "translate(-50%, 50%)",
1120
+ pointerEvents: "none"
1121
+ } }));
1122
+ if (isLoading) {
1123
+ return /* @__PURE__ */ React2.createElement("div", { className, style: backgroundStyle }, /* @__PURE__ */ React2.createElement(DecorativeBackground, null), /* @__PURE__ */ React2.createElement("div", { style: {
1124
+ position: "relative",
1125
+ zIndex: 10,
1126
+ display: "flex",
1127
+ alignItems: "center",
1128
+ justifyContent: "center",
1129
+ minHeight: "100vh"
1130
+ } }, /* @__PURE__ */ React2.createElement("div", { style: { textAlign: "center" } }, /* @__PURE__ */ React2.createElement("div", { style: {
1131
+ position: "relative",
1132
+ width: "64px",
1133
+ height: "64px",
1134
+ margin: "0 auto 16px"
1135
+ } }, /* @__PURE__ */ React2.createElement("div", { style: {
1136
+ position: "absolute",
1137
+ inset: 0,
1138
+ border: "4px solid #e9d5ff",
1139
+ borderRadius: "50%"
1140
+ } }), /* @__PURE__ */ React2.createElement("div", { style: {
1141
+ position: "absolute",
1142
+ inset: 0,
1143
+ border: "4px solid transparent",
1144
+ borderTopColor: "#a855f7",
1145
+ borderRadius: "50%",
1146
+ animation: "spin 1s linear infinite"
1147
+ } })), /* @__PURE__ */ React2.createElement("p", { style: { fontSize: "14px", color: "#6b7280" } }, "\u2728 \u52A0\u8F7D\u4E2D"))));
1148
+ }
1149
+ if (status === "completed" && result) {
1150
+ return /* @__PURE__ */ React2.createElement("div", { className, style: backgroundStyle }, /* @__PURE__ */ React2.createElement(DecorativeBackground, null), /* @__PURE__ */ React2.createElement("div", { style: {
1151
+ position: "relative",
1152
+ zIndex: 10,
1153
+ minHeight: "100vh",
1154
+ display: "flex",
1155
+ alignItems: "center",
1156
+ justifyContent: "center",
1157
+ padding: "24px"
1158
+ } }, /* @__PURE__ */ React2.createElement("div", { style: { maxWidth: "420px", width: "100%" } }, /* @__PURE__ */ React2.createElement("div", { style: {
1159
+ position: "relative",
1160
+ background: "linear-gradient(135deg, #fdf2f8 0%, #faf5ff 50%, #eff6ff 100%)",
1161
+ borderRadius: "32px",
1162
+ boxShadow: "0 25px 50px -12px rgba(168, 85, 247, 0.25), 0 0 0 1px rgba(168, 85, 247, 0.1)",
1163
+ overflow: "hidden",
1164
+ padding: "40px 32px",
1165
+ textAlign: "center"
1166
+ } }, /* @__PURE__ */ React2.createElement("div", { style: { position: "absolute", top: "20px", left: "20px", fontSize: "24px", opacity: 0.6 } }, "\u2728"), /* @__PURE__ */ React2.createElement("div", { style: { position: "absolute", top: "40px", right: "30px", fontSize: "20px", opacity: 0.5 } }, "\u2B50"), /* @__PURE__ */ React2.createElement("div", { style: { position: "absolute", bottom: "30px", left: "40px", fontSize: "18px", opacity: 0.4 } }, "\u{1F4AB}"), /* @__PURE__ */ React2.createElement("div", { style: { position: "absolute", bottom: "50px", right: "25px", fontSize: "22px", opacity: 0.5 } }, "\u{1F31F}"), /* @__PURE__ */ React2.createElement("div", { style: { marginBottom: "24px" } }, /* @__PURE__ */ React2.createElement("div", { style: {
1167
+ display: "inline-block",
1168
+ fontSize: "80px",
1169
+ animation: "bounce-slow 2s ease-in-out infinite",
1170
+ filter: "drop-shadow(0 10px 20px rgba(0,0,0,0.1))"
1171
+ } }, result.imageType === "emoji" ? result.image : "\u{1F389}")), /* @__PURE__ */ React2.createElement("h2", { style: {
1172
+ fontSize: "32px",
1173
+ fontWeight: 800,
1174
+ background: "linear-gradient(135deg, #9333ea 0%, #ec4899 100%)",
1175
+ WebkitBackgroundClip: "text",
1176
+ WebkitTextFillColor: "transparent",
1177
+ backgroundClip: "text",
1178
+ marginBottom: "16px",
1179
+ lineHeight: 1.3
1180
+ } }, result.title), /* @__PURE__ */ React2.createElement("div", { style: {
1181
+ background: "rgba(255, 255, 255, 0.8)",
1182
+ borderRadius: "20px",
1183
+ padding: "20px 24px",
1184
+ marginBottom: "28px",
1185
+ boxShadow: "0 4px 15px rgba(168, 85, 247, 0.1)",
1186
+ border: "2px dashed rgba(168, 85, 247, 0.2)"
1187
+ } }, /* @__PURE__ */ React2.createElement("p", { style: {
1188
+ fontSize: "16px",
1189
+ color: "#6b7280",
1190
+ lineHeight: 1.7,
1191
+ margin: 0
1192
+ } }, result.description)), /* @__PURE__ */ React2.createElement("div", { style: {
1193
+ display: "flex",
1194
+ alignItems: "center",
1195
+ justifyContent: "center",
1196
+ gap: "8px",
1197
+ marginBottom: "24px"
1198
+ } }, /* @__PURE__ */ React2.createElement("span", { style: { width: "40px", height: "3px", background: "linear-gradient(to right, #a855f7, transparent)", borderRadius: "999px" } }), /* @__PURE__ */ React2.createElement("span", { style: { fontSize: "16px" } }, "\u{1F495}"), /* @__PURE__ */ React2.createElement("span", { style: { width: "40px", height: "3px", background: "linear-gradient(to left, #ec4899, transparent)", borderRadius: "999px" } })), /* @__PURE__ */ React2.createElement(
1199
+ "button",
1200
+ {
1201
+ onClick: handleReset,
1202
+ style: {
1203
+ display: "inline-flex",
1204
+ alignItems: "center",
1205
+ justifyContent: "center",
1206
+ gap: "8px",
1207
+ padding: "14px 32px",
1208
+ fontSize: "16px",
1209
+ fontWeight: 600,
1210
+ color: "white",
1211
+ background: "linear-gradient(135deg, #a855f7 0%, #ec4899 100%)",
1212
+ border: "none",
1213
+ borderRadius: "9999px",
1214
+ cursor: "pointer",
1215
+ boxShadow: "0 10px 25px -5px rgba(168, 85, 247, 0.4)",
1216
+ transition: "all 0.3s ease"
1217
+ },
1218
+ onMouseEnter: (e) => {
1219
+ e.currentTarget.style.transform = "scale(1.05) translateY(-2px)";
1220
+ e.currentTarget.style.boxShadow = "0 15px 35px -5px rgba(168, 85, 247, 0.5)";
1221
+ },
1222
+ onMouseLeave: (e) => {
1223
+ e.currentTarget.style.transform = "scale(1) translateY(0)";
1224
+ e.currentTarget.style.boxShadow = "0 10px 25px -5px rgba(168, 85, 247, 0.4)";
1225
+ }
1226
+ },
1227
+ /* @__PURE__ */ React2.createElement("span", null, "\u{1F504}"),
1228
+ /* @__PURE__ */ React2.createElement("span", null, "\u91CD\u65B0\u6D4B\u8BD5")
1229
+ )))));
1230
+ }
1231
+ return /* @__PURE__ */ React2.createElement("div", { className, style: backgroundStyle }, /* @__PURE__ */ React2.createElement(DecorativeBackground, null), /* @__PURE__ */ React2.createElement("div", { style: {
1232
+ position: "relative",
1233
+ zIndex: 10,
1234
+ minHeight: "100vh",
1235
+ display: "flex",
1236
+ alignItems: "center",
1237
+ justifyContent: "center",
1238
+ padding: "16px"
1239
+ } }, /* @__PURE__ */ React2.createElement("div", { style: { maxWidth: "512px", width: "100%", textAlign: "center", userSelect: "none" } }, /* @__PURE__ */ React2.createElement("div", { style: { marginBottom: "48px" } }, /* @__PURE__ */ React2.createElement("div", { style: {
1240
+ display: "inline-block",
1241
+ marginBottom: "16px",
1242
+ animation: "bounce-slow 2s ease-in-out infinite"
1243
+ } }, /* @__PURE__ */ React2.createElement("span", { style: { fontSize: "56px" } }, "\u{1F3B2}")), /* @__PURE__ */ React2.createElement("h1", { style: {
1244
+ fontSize: "48px",
1245
+ fontWeight: 900,
1246
+ marginBottom: "12px",
1247
+ background: "linear-gradient(135deg, #9333ea 0%, #ec4899 50%, #3b82f6 100%)",
1248
+ WebkitBackgroundClip: "text",
1249
+ WebkitTextFillColor: "transparent",
1250
+ backgroundClip: "text",
1251
+ lineHeight: 1.2
1252
+ } }, gameTitle), gameDescription && /* @__PURE__ */ React2.createElement("p", { style: {
1253
+ fontSize: "18px",
1254
+ color: "#6b7280",
1255
+ fontWeight: 500
1256
+ } }, gameDescription)), /* @__PURE__ */ React2.createElement("div", { style: { marginBottom: "24px" } }, /* @__PURE__ */ React2.createElement(
1257
+ "button",
1258
+ {
1259
+ onMouseDown: handlePressStart,
1260
+ onMouseLeave: handleMouseLeave,
1261
+ onTouchStart: handlePressStart,
1262
+ onTouchEnd: handlePressEnd,
1263
+ onTouchMove: handleTouchMove,
1264
+ onTouchCancel: handlePressEnd,
1265
+ onContextMenu: (e) => e.preventDefault(),
1266
+ onDragStart: (e) => e.preventDefault(),
1267
+ style: {
1268
+ display: "block",
1269
+ margin: "0 auto",
1270
+ width: "200px",
1271
+ height: "200px",
1272
+ borderRadius: "50%",
1273
+ border: "none",
1274
+ fontSize: "20px",
1275
+ fontWeight: "bold",
1276
+ cursor: "pointer",
1277
+ position: "relative",
1278
+ overflow: "hidden",
1279
+ userSelect: "none",
1280
+ WebkitTouchCallout: "none",
1281
+ WebkitUserSelect: "none",
1282
+ touchAction: "none",
1283
+ transition: "transform 0.3s ease",
1284
+ transform: status === "pressing" ? "scale(0.95)" : "scale(1)",
1285
+ background: status === "pressing" ? "linear-gradient(to top, rgb(168, 85, 247) " + pressProgress + "%, rgb(236, 72, 153) " + pressProgress + "%)" : "linear-gradient(135deg, #4f46e5 0%, #7c3aed 25%, #db2777 50%, #f97316 75%, #059669 100%)",
1286
+ boxShadow: status === "pressing" ? "inset 0 4px 12px rgba(0,0,0,0.3), 0 0 0 4px rgba(168, 85, 247, 0.5)" : "0 15px 35px -10px rgba(79, 70, 229, 0.6), 0 0 0 4px rgba(255,255,255,0.8)"
1287
+ }
1288
+ },
1289
+ /* @__PURE__ */ React2.createElement("div", { style: {
1290
+ position: "absolute",
1291
+ top: 0,
1292
+ left: 0,
1293
+ right: 0,
1294
+ bottom: 0,
1295
+ display: "flex",
1296
+ flexDirection: "column",
1297
+ alignItems: "center",
1298
+ justifyContent: "center",
1299
+ color: "white",
1300
+ pointerEvents: "none"
1301
+ } }, status === "pressing" ? /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("span", { style: { fontSize: "36px", fontWeight: 900, marginBottom: "4px" } }, Math.round(pressProgress), "%"), /* @__PURE__ */ React2.createElement("span", { style: { fontSize: "14px", opacity: 0.8 } }, "\u7EE7\u7EED\u6309\u4F4F")) : /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("span", { style: { fontSize: "32px", marginBottom: "8px" } }, "\u{1F446}"), /* @__PURE__ */ React2.createElement("span", { style: { fontSize: "16px", fontWeight: "bold", padding: "0 16px" } }, buttonText))),
1302
+ status === "idle" && /* @__PURE__ */ React2.createElement("div", { style: {
1303
+ position: "absolute",
1304
+ top: "16px",
1305
+ left: "16px",
1306
+ right: "16px",
1307
+ bottom: "16px",
1308
+ border: "2px solid rgba(255,255,255,0.3)",
1309
+ borderRadius: "50%"
1310
+ } })
1311
+ ), status === "pressing" && /* @__PURE__ */ React2.createElement("div", { style: {
1312
+ marginTop: "16px",
1313
+ marginLeft: "auto",
1314
+ marginRight: "auto",
1315
+ width: "192px",
1316
+ height: "8px",
1317
+ backgroundColor: "#e5e7eb",
1318
+ borderRadius: "9999px",
1319
+ overflow: "hidden"
1320
+ } }, /* @__PURE__ */ React2.createElement("div", { style: {
1321
+ height: "100%",
1322
+ width: pressProgress + "%",
1323
+ background: "linear-gradient(to right, #a855f7, #ec4899)",
1324
+ transition: "width 0.1s ease"
1325
+ } }))), /* @__PURE__ */ React2.createElement("div", { style: { marginTop: "24px" } }, status === "pressing" ? /* @__PURE__ */ React2.createElement("p", { style: {
1326
+ fontSize: "18px",
1327
+ fontWeight: 500,
1328
+ color: "#9333ea",
1329
+ animation: "pulse 2s ease-in-out infinite"
1330
+ } }, "\u2728 \u6B63\u5728\u5206\u6790\u4E2D...") : /* @__PURE__ */ React2.createElement("div", { style: {
1331
+ display: "flex",
1332
+ alignItems: "center",
1333
+ justifyContent: "center",
1334
+ gap: "8px"
1335
+ } }, /* @__PURE__ */ React2.createElement("span", { style: {
1336
+ display: "inline-block",
1337
+ width: "6px",
1338
+ height: "6px",
1339
+ backgroundColor: "#a855f7",
1340
+ borderRadius: "50%",
1341
+ animation: "bounce 1s infinite"
1342
+ } }), /* @__PURE__ */ React2.createElement("span", { style: {
1343
+ display: "inline-block",
1344
+ width: "6px",
1345
+ height: "6px",
1346
+ backgroundColor: "#ec4899",
1347
+ borderRadius: "50%",
1348
+ animation: "bounce 1s infinite 0.1s"
1349
+ } }), /* @__PURE__ */ React2.createElement("span", { style: {
1350
+ display: "inline-block",
1351
+ width: "6px",
1352
+ height: "6px",
1353
+ backgroundColor: "#3b82f6",
1354
+ borderRadius: "50%",
1355
+ animation: "bounce 1s infinite 0.2s"
1356
+ } }))))));
1357
+ };
1358
+ var touchOptimizationStyles = `
1359
+ @keyframes bounce-slow {
1360
+ 0%, 100% {
1361
+ transform: translateY(0);
1362
+ }
1363
+ 50% {
1364
+ transform: translateY(-10px);
1365
+ }
1366
+ }
1367
+
1368
+ @keyframes bounce {
1369
+ 0%, 100% {
1370
+ transform: translateY(0);
1371
+ }
1372
+ 50% {
1373
+ transform: translateY(-4px);
1374
+ }
1375
+ }
1376
+
1377
+ @keyframes pulse {
1378
+ 0%, 100% {
1379
+ opacity: 1;
1380
+ }
1381
+ 50% {
1382
+ opacity: 0.5;
1383
+ }
1384
+ }
1385
+
1386
+ @keyframes spin {
1387
+ from {
1388
+ transform: rotate(0deg);
1389
+ }
1390
+ to {
1391
+ transform: rotate(360deg);
1392
+ }
1393
+ }
1394
+ `;
1395
+ if (typeof document !== "undefined" && !document.getElementById("test-yourself-styles")) {
1396
+ const style = document.createElement("style");
1397
+ style.id = "test-yourself-styles";
1398
+ style.textContent = touchOptimizationStyles;
1399
+ document.head.appendChild(style);
1400
+ }
1401
+ var ConfigManager = ({
1402
+ configService,
1403
+ onConfigChange,
1404
+ className = "",
1405
+ onImageUpload
1406
+ }) => {
1407
+ const [configs, setConfigs] = useState([]);
1408
+ const [selectedConfig, setSelectedConfig] = useState(null);
1409
+ const [isEditing, setIsEditing] = useState(false);
1410
+ const [isCreating, setIsCreating] = useState(false);
1411
+ const [loading, setLoading] = useState(true);
1412
+ const [error, setError] = useState(null);
1413
+ const [success, setSuccess] = useState(null);
1414
+ const [editForm, setEditForm] = useState({
1415
+ name: "",
1416
+ description: "",
1417
+ gameTitle: "",
1418
+ gameDescription: "",
1419
+ buttonText: "\u957F\u6309\u5F00\u59CB\u6D4B\u8BD5",
1420
+ longPressDuration: 2e3,
1421
+ results: []
1422
+ });
1423
+ const loadConfigs = useCallback(async () => {
1424
+ try {
1425
+ setLoading(true);
1426
+ const allConfigs = await configService.getAllConfigs();
1427
+ setConfigs(allConfigs);
1428
+ onConfigChange?.(allConfigs);
1429
+ } catch (err) {
1430
+ setError("\u52A0\u8F7D\u914D\u7F6E\u5931\u8D25: " + err.message);
1431
+ } finally {
1432
+ setLoading(false);
1433
+ }
1434
+ }, [configService, onConfigChange]);
1435
+ useEffect(() => {
1436
+ loadConfigs();
1437
+ }, [loadConfigs]);
1438
+ const handleStartCreate = () => {
1439
+ setEditForm({
1440
+ name: "\u65B0\u914D\u7F6E",
1441
+ description: "",
1442
+ gameTitle: "\u6D4B\u6D4B\u4F60\u662F\u4EC0\u4E48",
1443
+ gameDescription: "\u957F\u6309\u6309\u94AE\uFF0C\u53D1\u73B0\u4F60\u7684\u4E13\u5C5E\u5C5E\u6027",
1444
+ buttonText: "\u957F\u6309\u5F00\u59CB\u6D4B\u8BD5",
1445
+ longPressDuration: 2e3,
1446
+ results: []
1447
+ });
1448
+ setIsCreating(true);
1449
+ setIsEditing(true);
1450
+ setSelectedConfig(null);
1451
+ };
1452
+ const handleStartEdit = (config) => {
1453
+ setEditForm({
1454
+ name: config.name,
1455
+ description: config.description || "",
1456
+ gameTitle: config.config.gameTitle,
1457
+ gameDescription: config.config.gameDescription || "",
1458
+ buttonText: config.config.buttonText || "\u957F\u6309\u5F00\u59CB\u6D4B\u8BD5",
1459
+ longPressDuration: config.config.longPressDuration || 2e3,
1460
+ results: config.config.results
1461
+ });
1462
+ setIsCreating(false);
1463
+ setIsEditing(true);
1464
+ setSelectedConfig(config);
1465
+ };
1466
+ const handleCancelEdit = () => {
1467
+ setIsEditing(false);
1468
+ setIsCreating(false);
1469
+ setSelectedConfig(null);
1470
+ setError(null);
1471
+ };
1472
+ const handleSave = async () => {
1473
+ try {
1474
+ setError(null);
1475
+ if (!editForm.name.trim()) {
1476
+ setError("\u8BF7\u8F93\u5165\u914D\u7F6E\u540D\u79F0");
1477
+ return;
1478
+ }
1479
+ if (!editForm.gameTitle.trim()) {
1480
+ setError("\u8BF7\u8F93\u5165\u6E38\u620F\u6807\u9898");
1481
+ return;
1482
+ }
1483
+ if (editForm.results.length === 0) {
1484
+ setError("\u8BF7\u81F3\u5C11\u6DFB\u52A0\u4E00\u4E2A\u7ED3\u679C\u9879");
1485
+ return;
1486
+ }
1487
+ const testConfig = {
1488
+ gameTitle: editForm.gameTitle,
1489
+ gameDescription: editForm.gameDescription,
1490
+ buttonText: editForm.buttonText,
1491
+ longPressDuration: editForm.longPressDuration,
1492
+ results: editForm.results
1493
+ };
1494
+ if (isCreating) {
1495
+ await configService.createConfig(
1496
+ editForm.name,
1497
+ testConfig,
1498
+ editForm.description
1499
+ );
1500
+ setSuccess("\u521B\u5EFA\u914D\u7F6E\u6210\u529F\uFF01");
1501
+ } else if (selectedConfig) {
1502
+ await configService.updateConfig(selectedConfig.id, {
1503
+ name: editForm.name,
1504
+ description: editForm.description,
1505
+ config: testConfig
1506
+ });
1507
+ setSuccess("\u66F4\u65B0\u914D\u7F6E\u6210\u529F\uFF01");
1508
+ }
1509
+ await loadConfigs();
1510
+ handleCancelEdit();
1511
+ setTimeout(() => setSuccess(null), 3e3);
1512
+ } catch (err) {
1513
+ setError("\u4FDD\u5B58\u5931\u8D25: " + err.message);
1514
+ }
1515
+ };
1516
+ const handleDelete = async (id) => {
1517
+ if (!confirm("\u786E\u5B9A\u8981\u5220\u9664\u8FD9\u4E2A\u914D\u7F6E\u5417\uFF1F")) return;
1518
+ try {
1519
+ await configService.deleteConfig(id);
1520
+ await loadConfigs();
1521
+ setSuccess("\u5220\u9664\u6210\u529F\uFF01");
1522
+ setTimeout(() => setSuccess(null), 3e3);
1523
+ } catch (err) {
1524
+ setError("\u5220\u9664\u5931\u8D25: " + err.message);
1525
+ }
1526
+ };
1527
+ const handleDuplicate = async (id) => {
1528
+ try {
1529
+ await configService.duplicateConfig(id);
1530
+ await loadConfigs();
1531
+ setSuccess("\u590D\u5236\u6210\u529F\uFF01");
1532
+ setTimeout(() => setSuccess(null), 3e3);
1533
+ } catch (err) {
1534
+ setError("\u590D\u5236\u5931\u8D25: " + err.message);
1535
+ }
1536
+ };
1537
+ const handleSetDefault = async (id) => {
1538
+ try {
1539
+ await configService.setDefaultConfig(id);
1540
+ await loadConfigs();
1541
+ setSuccess("\u8BBE\u7F6E\u9ED8\u8BA4\u914D\u7F6E\u6210\u529F\uFF01");
1542
+ setTimeout(() => setSuccess(null), 3e3);
1543
+ } catch (err) {
1544
+ setError("\u8BBE\u7F6E\u5931\u8D25: " + err.message);
1545
+ }
1546
+ };
1547
+ const handleExport = async (id) => {
1548
+ try {
1549
+ const jsonString = await configService.exportConfig(id);
1550
+ const blob = new Blob([jsonString], { type: "application/json" });
1551
+ const url = URL.createObjectURL(blob);
1552
+ const a = document.createElement("a");
1553
+ a.href = url;
1554
+ a.download = "config_" + id + ".json";
1555
+ document.body.appendChild(a);
1556
+ a.click();
1557
+ document.body.removeChild(a);
1558
+ URL.revokeObjectURL(url);
1559
+ setSuccess("\u5BFC\u51FA\u6210\u529F\uFF01");
1560
+ setTimeout(() => setSuccess(null), 3e3);
1561
+ } catch (err) {
1562
+ setError("\u5BFC\u51FA\u5931\u8D25: " + err.message);
1563
+ }
1564
+ };
1565
+ const handleImport = async () => {
1566
+ const input = document.createElement("input");
1567
+ input.type = "file";
1568
+ input.accept = ".json";
1569
+ input.onchange = async (e) => {
1570
+ const file = e.target.files?.[0];
1571
+ if (!file) return;
1572
+ try {
1573
+ const text2 = await file.text();
1574
+ await configService.importConfig(text2);
1575
+ await loadConfigs();
1576
+ setSuccess("\u5BFC\u5165\u6210\u529F\uFF01");
1577
+ setTimeout(() => setSuccess(null), 3e3);
1578
+ } catch (err) {
1579
+ setError("\u5BFC\u5165\u5931\u8D25: " + err.message);
1580
+ }
1581
+ };
1582
+ input.click();
1583
+ };
1584
+ const handleAddResult = () => {
1585
+ const newResult = {
1586
+ id: "temp_" + Date.now(),
1587
+ _tempId: "temp_" + Date.now(),
1588
+ title: "\u65B0\u7ED3\u679C",
1589
+ description: "\u8FD9\u662F\u4E00\u4E2A\u65B0\u7684\u7ED3\u679C\u63CF\u8FF0",
1590
+ image: "\u{1F389}",
1591
+ imageType: "emoji"
1592
+ };
1593
+ setEditForm({
1594
+ ...editForm,
1595
+ results: [...editForm.results, newResult]
1596
+ });
1597
+ };
1598
+ const handleDeleteResult = (index2) => {
1599
+ const newResults = [...editForm.results];
1600
+ newResults.splice(index2, 1);
1601
+ setEditForm({ ...editForm, results: newResults });
1602
+ };
1603
+ const handleUpdateResult = (index2, updates) => {
1604
+ const newResults = [...editForm.results];
1605
+ newResults[index2] = { ...newResults[index2], ...updates };
1606
+ setEditForm({ ...editForm, results: newResults });
1607
+ };
1608
+ const handleResultImageUpload = async (index2, file) => {
1609
+ try {
1610
+ if (!onImageUpload) {
1611
+ const reader = new FileReader();
1612
+ reader.onload = (e) => {
1613
+ const base64 = e.target?.result;
1614
+ handleUpdateResult(index2, {
1615
+ image: base64,
1616
+ imageType: "url"
1617
+ });
1618
+ };
1619
+ reader.readAsDataURL(file);
1620
+ } else {
1621
+ const url = await onImageUpload(file);
1622
+ handleUpdateResult(index2, {
1623
+ image: url,
1624
+ imageType: "url"
1625
+ });
1626
+ }
1627
+ } catch (err) {
1628
+ setError("\u4E0A\u4F20\u56FE\u7247\u5931\u8D25: " + err.message);
1629
+ }
1630
+ };
1631
+ const handleUseDefaultResults = () => {
1632
+ if (confirm("\u786E\u5B9A\u8981\u4F7F\u7528\u9ED8\u8BA4\u768445\u4E2A\u7ED3\u679C\u5417\uFF1F\u8FD9\u5C06\u66FF\u6362\u5F53\u524D\u7684\u6240\u6709\u7ED3\u679C\u3002")) {
1633
+ setEditForm({ ...editForm, results: DEFAULT_RESULTS });
1634
+ }
1635
+ };
1636
+ const renderConfigList = () => /* @__PURE__ */ React2.createElement("div", { className: "space-y-4" }, configs.map((config) => /* @__PURE__ */ React2.createElement(
1637
+ "div",
1638
+ {
1639
+ key: config.id,
1640
+ className: "bg-white dark:bg-gray-800 rounded-lg shadow-sm p-4 border border-gray-200 dark:border-gray-700 hover:shadow-md transition-shadow"
1641
+ },
1642
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-start justify-between" }, /* @__PURE__ */ React2.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React2.createElement("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white" }, config.name), config.isDefault && /* @__PURE__ */ React2.createElement("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200" }, /* @__PURE__ */ React2.createElement(Star, { className: "w-3 h-3" }), "\u9ED8\u8BA4")), config.description && /* @__PURE__ */ React2.createElement("p", { className: "text-sm text-gray-600 dark:text-gray-400 mb-2" }, config.description), /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-4 text-xs text-gray-500 dark:text-gray-400" }, /* @__PURE__ */ React2.createElement("span", null, "\u7ED3\u679C\u6570: ", config.config.results.length), /* @__PURE__ */ React2.createElement("span", null, "\u521B\u5EFA: ", new Date(config.createdAt).toLocaleDateString()))), /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React2.createElement(
1643
+ "button",
1644
+ {
1645
+ onClick: () => handleSetDefault(config.id),
1646
+ disabled: config.isDefault,
1647
+ className: "p-2 text-gray-600 hover:text-yellow-600 disabled:opacity-50 disabled:cursor-not-allowed",
1648
+ title: "\u8BBE\u4E3A\u9ED8\u8BA4"
1649
+ },
1650
+ /* @__PURE__ */ React2.createElement(Star, { className: "w-5 h-5" })
1651
+ ), /* @__PURE__ */ React2.createElement(
1652
+ "button",
1653
+ {
1654
+ onClick: () => handleStartEdit(config),
1655
+ className: "p-2 text-gray-600 hover:text-blue-600",
1656
+ title: "\u7F16\u8F91"
1657
+ },
1658
+ /* @__PURE__ */ React2.createElement(Edit, { className: "w-5 h-5" })
1659
+ ), /* @__PURE__ */ React2.createElement(
1660
+ "button",
1661
+ {
1662
+ onClick: () => handleDuplicate(config.id),
1663
+ className: "p-2 text-gray-600 hover:text-green-600",
1664
+ title: "\u590D\u5236"
1665
+ },
1666
+ /* @__PURE__ */ React2.createElement(Copy, { className: "w-5 h-5" })
1667
+ ), /* @__PURE__ */ React2.createElement(
1668
+ "button",
1669
+ {
1670
+ onClick: () => handleExport(config.id),
1671
+ className: "p-2 text-gray-600 hover:text-purple-600",
1672
+ title: "\u5BFC\u51FA"
1673
+ },
1674
+ /* @__PURE__ */ React2.createElement(Download, { className: "w-5 h-5" })
1675
+ ), /* @__PURE__ */ React2.createElement(
1676
+ "button",
1677
+ {
1678
+ onClick: () => handleDelete(config.id),
1679
+ className: "p-2 text-gray-600 hover:text-red-600",
1680
+ title: "\u5220\u9664"
1681
+ },
1682
+ /* @__PURE__ */ React2.createElement(Trash2, { className: "w-5 h-5" })
1683
+ )))
1684
+ )));
1685
+ const renderResultEditor = (result, index2) => /* @__PURE__ */ React2.createElement(
1686
+ "div",
1687
+ {
1688
+ key: result._tempId || result.id,
1689
+ className: "bg-gray-50 dark:bg-gray-700 rounded-lg p-4 space-y-3"
1690
+ },
1691
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-start justify-between" }, /* @__PURE__ */ React2.createElement("h4", { className: "text-sm font-medium text-gray-700 dark:text-gray-300" }, "\u7ED3\u679C #", index2 + 1), /* @__PURE__ */ React2.createElement(
1692
+ "button",
1693
+ {
1694
+ onClick: () => handleDeleteResult(index2),
1695
+ className: "text-red-600 hover:text-red-700"
1696
+ },
1697
+ /* @__PURE__ */ React2.createElement(Trash2, { className: "w-4 h-4" })
1698
+ )),
1699
+ /* @__PURE__ */ React2.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3" }, /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1" }, "\u6807\u9898"), /* @__PURE__ */ React2.createElement(
1700
+ "input",
1701
+ {
1702
+ type: "text",
1703
+ value: result.title,
1704
+ onChange: (e) => handleUpdateResult(index2, { title: e.target.value }),
1705
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white",
1706
+ placeholder: "\u4F8B\u5982: \u53EF\u7231\u7684\u732B\u54AA"
1707
+ }
1708
+ )), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1" }, "\u56FE\u7247/Emoji"), /* @__PURE__ */ React2.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React2.createElement(
1709
+ "input",
1710
+ {
1711
+ type: "text",
1712
+ value: result.image,
1713
+ onChange: (e) => handleUpdateResult(index2, { image: e.target.value }),
1714
+ className: "flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white",
1715
+ placeholder: "\u{1F389} \u6216 URL"
1716
+ }
1717
+ ), /* @__PURE__ */ React2.createElement("label", { className: "cursor-pointer inline-flex items-center justify-center px-3 py-2 bg-blue-50 dark:bg-blue-900 text-blue-600 dark:text-blue-200 rounded-lg hover:bg-blue-100 dark:hover:bg-blue-800" }, /* @__PURE__ */ React2.createElement(Image, { className: "w-5 h-5" }), /* @__PURE__ */ React2.createElement(
1718
+ "input",
1719
+ {
1720
+ type: "file",
1721
+ accept: "image/*",
1722
+ className: "hidden",
1723
+ onChange: (e) => {
1724
+ const file = e.target.files?.[0];
1725
+ if (file) handleResultImageUpload(index2, file);
1726
+ }
1727
+ }
1728
+ ))))),
1729
+ /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1" }, "\u63CF\u8FF0"), /* @__PURE__ */ React2.createElement(
1730
+ "textarea",
1731
+ {
1732
+ value: result.description,
1733
+ onChange: (e) => handleUpdateResult(index2, { description: e.target.value }),
1734
+ rows: 2,
1735
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white",
1736
+ placeholder: "\u8FD9\u4E2A\u7ED3\u679C\u7684\u8BE6\u7EC6\u63CF\u8FF0..."
1737
+ }
1738
+ )),
1739
+ result.image && /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2 p-2 bg-white dark:bg-gray-600 rounded border border-gray-200 dark:border-gray-500" }, /* @__PURE__ */ React2.createElement("span", { className: "text-sm text-gray-500 dark:text-gray-400" }, "\u9884\u89C8:"), result.imageType === "emoji" ? /* @__PURE__ */ React2.createElement("span", { className: "text-2xl" }, result.image) : /* @__PURE__ */ React2.createElement("img", { src: result.image, alt: result.title, className: "w-8 h-8 object-cover rounded" }), /* @__PURE__ */ React2.createElement("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300" }, result.title))
1740
+ );
1741
+ const renderEditForm = () => /* @__PURE__ */ React2.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("h2", { className: "text-2xl font-bold text-gray-900 dark:text-white" }, isCreating ? "\u521B\u5EFA\u65B0\u914D\u7F6E" : "\u7F16\u8F91\u914D\u7F6E"), /* @__PURE__ */ React2.createElement(
1742
+ "button",
1743
+ {
1744
+ onClick: handleCancelEdit,
1745
+ className: "p-2 text-gray-600 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-200"
1746
+ },
1747
+ /* @__PURE__ */ React2.createElement(X, { className: "w-6 h-6" })
1748
+ )), /* @__PURE__ */ React2.createElement("div", { className: "space-y-4 bg-white dark:bg-gray-800 rounded-lg p-6 shadow-sm border border-gray-200 dark:border-gray-700" }, /* @__PURE__ */ React2.createElement("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white mb-4" }, "\u57FA\u672C\u4FE1\u606F"), /* @__PURE__ */ React2.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u914D\u7F6E\u540D\u79F0 *"), /* @__PURE__ */ React2.createElement(
1749
+ "input",
1750
+ {
1751
+ type: "text",
1752
+ value: editForm.name,
1753
+ onChange: (e) => setEditForm({ ...editForm, name: e.target.value }),
1754
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white",
1755
+ placeholder: "\u4F8B\u5982: \u52A8\u7269\u4E3B\u9898\u6D4B\u8BD5"
1756
+ }
1757
+ )), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u6E38\u620F\u6807\u9898 *"), /* @__PURE__ */ React2.createElement(
1758
+ "input",
1759
+ {
1760
+ type: "text",
1761
+ value: editForm.gameTitle,
1762
+ onChange: (e) => setEditForm({ ...editForm, gameTitle: e.target.value }),
1763
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white",
1764
+ placeholder: "\u4F8B\u5982: \u6D4B\u6D4B\u4F60\u662F\u4EC0\u4E48\u52A8\u7269"
1765
+ }
1766
+ ))), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u914D\u7F6E\u63CF\u8FF0"), /* @__PURE__ */ React2.createElement(
1767
+ "textarea",
1768
+ {
1769
+ value: editForm.description,
1770
+ onChange: (e) => setEditForm({ ...editForm, description: e.target.value }),
1771
+ rows: 2,
1772
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white",
1773
+ placeholder: "\u7B80\u8981\u63CF\u8FF0\u8FD9\u4E2A\u914D\u7F6E\u7684\u7528\u9014..."
1774
+ }
1775
+ )), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u6E38\u620F\u63CF\u8FF0"), /* @__PURE__ */ React2.createElement(
1776
+ "input",
1777
+ {
1778
+ type: "text",
1779
+ value: editForm.gameDescription,
1780
+ onChange: (e) => setEditForm({ ...editForm, gameDescription: e.target.value }),
1781
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white",
1782
+ placeholder: "\u4F8B\u5982: \u957F\u6309\u6309\u94AE\uFF0C\u53D1\u73B0\u4F60\u7684\u4E13\u5C5E\u5C5E\u6027"
1783
+ }
1784
+ )), /* @__PURE__ */ React2.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u6309\u94AE\u6587\u672C"), /* @__PURE__ */ React2.createElement(
1785
+ "input",
1786
+ {
1787
+ type: "text",
1788
+ value: editForm.buttonText,
1789
+ onChange: (e) => setEditForm({ ...editForm, buttonText: e.target.value }),
1790
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white"
1791
+ }
1792
+ )), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u957F\u6309\u65F6\u95F4 (\u6BEB\u79D2)"), /* @__PURE__ */ React2.createElement(
1793
+ "input",
1794
+ {
1795
+ type: "number",
1796
+ value: editForm.longPressDuration,
1797
+ onChange: (e) => setEditForm({ ...editForm, longPressDuration: parseInt(e.target.value) || 2e3 }),
1798
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white",
1799
+ min: "500",
1800
+ step: "100"
1801
+ }
1802
+ )))), /* @__PURE__ */ React2.createElement("div", { className: "bg-white dark:bg-gray-800 rounded-lg p-6 shadow-sm border border-gray-200 dark:border-gray-700" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between mb-4" }, /* @__PURE__ */ React2.createElement("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white" }, "\u7ED3\u679C\u5217\u8868 (", editForm.results.length, ")"), /* @__PURE__ */ React2.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React2.createElement(
1803
+ "button",
1804
+ {
1805
+ onClick: handleUseDefaultResults,
1806
+ className: "inline-flex items-center gap-2 px-3 py-1.5 text-sm bg-purple-50 dark:bg-purple-900 text-purple-600 dark:text-purple-200 rounded-lg hover:bg-purple-100 dark:hover:bg-purple-800"
1807
+ },
1808
+ "\u4F7F\u7528\u9ED8\u8BA4\u7ED3\u679C"
1809
+ ), /* @__PURE__ */ React2.createElement(
1810
+ "button",
1811
+ {
1812
+ onClick: handleAddResult,
1813
+ className: "inline-flex items-center gap-2 px-3 py-1.5 text-sm bg-blue-50 dark:bg-blue-900 text-blue-600 dark:text-blue-200 rounded-lg hover:bg-blue-100 dark:hover:bg-blue-800"
1814
+ },
1815
+ /* @__PURE__ */ React2.createElement(Plus, { className: "w-4 h-4" }),
1816
+ "\u6DFB\u52A0\u7ED3\u679C"
1817
+ ))), /* @__PURE__ */ React2.createElement("div", { className: "space-y-3 max-h-[500px] overflow-y-auto" }, editForm.results.length === 0 ? /* @__PURE__ */ React2.createElement("div", { className: "text-center py-8 text-gray-500 dark:text-gray-400" }, /* @__PURE__ */ React2.createElement("p", null, "\u8FD8\u6CA1\u6709\u6DFB\u52A0\u4EFB\u4F55\u7ED3\u679C"), /* @__PURE__ */ React2.createElement("p", { className: "text-sm mt-2" }, '\u70B9\u51FB"\u6DFB\u52A0\u7ED3\u679C"\u6216"\u4F7F\u7528\u9ED8\u8BA4\u7ED3\u679C"\u5F00\u59CB')) : editForm.results.map((result, index2) => renderResultEditor(result, index2)))), /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-end gap-3" }, /* @__PURE__ */ React2.createElement(
1818
+ "button",
1819
+ {
1820
+ onClick: handleCancelEdit,
1821
+ className: "px-6 py-2 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700"
1822
+ },
1823
+ "\u53D6\u6D88"
1824
+ ), /* @__PURE__ */ React2.createElement(
1825
+ "button",
1826
+ {
1827
+ onClick: handleSave,
1828
+ className: "inline-flex items-center gap-2 px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
1829
+ },
1830
+ /* @__PURE__ */ React2.createElement(Save, { className: "w-5 h-5" }),
1831
+ "\u4FDD\u5B58\u914D\u7F6E"
1832
+ )));
1833
+ if (loading) {
1834
+ return /* @__PURE__ */ React2.createElement("div", { className: clsx("p-8", className) }, /* @__PURE__ */ React2.createElement("div", { className: "text-center" }, /* @__PURE__ */ React2.createElement("div", { className: "inline-block w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ React2.createElement("p", { className: "mt-2 text-gray-600 dark:text-gray-400" }, "\u52A0\u8F7D\u4E2D...")));
1835
+ }
1836
+ return /* @__PURE__ */ React2.createElement("div", { className: clsx("p-6", className) }, error && /* @__PURE__ */ React2.createElement("div", { className: "mb-4 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg flex items-center gap-2 text-red-800 dark:text-red-200" }, /* @__PURE__ */ React2.createElement(AlertCircle, { className: "w-5 h-5 flex-shrink-0" }), /* @__PURE__ */ React2.createElement("span", null, error), /* @__PURE__ */ React2.createElement("button", { onClick: () => setError(null), className: "ml-auto" }, /* @__PURE__ */ React2.createElement(X, { className: "w-5 h-5" }))), success && /* @__PURE__ */ React2.createElement("div", { className: "mb-4 p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg flex items-center gap-2 text-green-800 dark:text-green-200" }, /* @__PURE__ */ React2.createElement(CheckCircle2, { className: "w-5 h-5 flex-shrink-0" }), /* @__PURE__ */ React2.createElement("span", null, success), /* @__PURE__ */ React2.createElement("button", { onClick: () => setSuccess(null), className: "ml-auto" }, /* @__PURE__ */ React2.createElement(X, { className: "w-5 h-5" }))), isEditing ? renderEditForm() : /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between mb-6" }, /* @__PURE__ */ React2.createElement("h1", { className: "text-3xl font-bold text-gray-900 dark:text-white" }, "\u914D\u7F6E\u7BA1\u7406"), /* @__PURE__ */ React2.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React2.createElement(
1837
+ "button",
1838
+ {
1839
+ onClick: handleImport,
1840
+ className: "inline-flex items-center gap-2 px-4 py-2 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700"
1841
+ },
1842
+ /* @__PURE__ */ React2.createElement(Upload, { className: "w-5 h-5" }),
1843
+ "\u5BFC\u5165"
1844
+ ), /* @__PURE__ */ React2.createElement(
1845
+ "button",
1846
+ {
1847
+ onClick: handleStartCreate,
1848
+ className: "inline-flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
1849
+ },
1850
+ /* @__PURE__ */ React2.createElement(Plus, { className: "w-5 h-5" }),
1851
+ "\u521B\u5EFA\u914D\u7F6E"
1852
+ ))), configs.length === 0 ? /* @__PURE__ */ React2.createElement("div", { className: "text-center py-12 bg-white dark:bg-gray-800 rounded-lg border-2 border-dashed border-gray-300 dark:border-gray-700" }, /* @__PURE__ */ React2.createElement("p", { className: "text-gray-600 dark:text-gray-400 mb-4" }, "\u8FD8\u6CA1\u6709\u4EFB\u4F55\u914D\u7F6E"), /* @__PURE__ */ React2.createElement(
1853
+ "button",
1854
+ {
1855
+ onClick: handleStartCreate,
1856
+ className: "inline-flex items-center gap-2 px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
1857
+ },
1858
+ /* @__PURE__ */ React2.createElement(Plus, { className: "w-5 h-5" }),
1859
+ "\u521B\u5EFA\u7B2C\u4E00\u4E2A\u914D\u7F6E"
1860
+ )) : renderConfigList()));
1861
+ };
1862
+ var ConfigList = ({
1863
+ configService,
1864
+ onSelect,
1865
+ onEdit,
1866
+ onDelete,
1867
+ showActions = true,
1868
+ showPreviewLink = false,
1869
+ previewBaseUrl = "/test-yourself",
1870
+ className = "",
1871
+ pageSize = 10
1872
+ }) => {
1873
+ const [configs, setConfigs] = useState([]);
1874
+ const [loading, setLoading] = useState(true);
1875
+ const [currentPage, setCurrentPage] = useState(1);
1876
+ const [searchTerm, setSearchTerm] = useState("");
1877
+ const loadConfigs = useCallback(async () => {
1878
+ try {
1879
+ setLoading(true);
1880
+ const list = await configService.getConfigList();
1881
+ setConfigs(list);
1882
+ } catch (error) {
1883
+ console.error("\u52A0\u8F7D\u914D\u7F6E\u5217\u8868\u5931\u8D25:", error);
1884
+ } finally {
1885
+ setLoading(false);
1886
+ }
1887
+ }, [configService]);
1888
+ useEffect(() => {
1889
+ loadConfigs();
1890
+ }, [loadConfigs]);
1891
+ const handleRefresh = () => {
1892
+ loadConfigs();
1893
+ };
1894
+ const handleSelect = (id) => {
1895
+ onSelect?.(id);
1896
+ };
1897
+ const handleEdit = (id) => {
1898
+ onEdit?.(id);
1899
+ };
1900
+ const handleDelete = async (id) => {
1901
+ if (!confirm("\u786E\u5B9A\u8981\u5220\u9664\u8FD9\u4E2A\u914D\u7F6E\u5417\uFF1F")) return;
1902
+ try {
1903
+ await configService.deleteConfig(id);
1904
+ await loadConfigs();
1905
+ onDelete?.(id);
1906
+ } catch (error) {
1907
+ console.error("\u5220\u9664\u914D\u7F6E\u5931\u8D25:", error);
1908
+ alert("\u5220\u9664\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
1909
+ }
1910
+ };
1911
+ const handleDuplicate = async (id) => {
1912
+ try {
1913
+ await configService.duplicateConfig(id);
1914
+ await loadConfigs();
1915
+ } catch (error) {
1916
+ console.error("\u590D\u5236\u914D\u7F6E\u5931\u8D25:", error);
1917
+ alert("\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
1918
+ }
1919
+ };
1920
+ const handleSetDefault = async (id) => {
1921
+ try {
1922
+ await configService.setDefaultConfig(id);
1923
+ await loadConfigs();
1924
+ } catch (error) {
1925
+ console.error("\u8BBE\u7F6E\u9ED8\u8BA4\u914D\u7F6E\u5931\u8D25:", error);
1926
+ alert("\u8BBE\u7F6E\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
1927
+ }
1928
+ };
1929
+ const handleExport = async (id) => {
1930
+ try {
1931
+ const jsonString = await configService.exportConfig(id);
1932
+ const blob = new Blob([jsonString], { type: "application/json" });
1933
+ const url = URL.createObjectURL(blob);
1934
+ const a = document.createElement("a");
1935
+ a.href = url;
1936
+ a.download = "config_" + id + ".json";
1937
+ document.body.appendChild(a);
1938
+ a.click();
1939
+ document.body.removeChild(a);
1940
+ URL.revokeObjectURL(url);
1941
+ } catch (error) {
1942
+ console.error("\u5BFC\u51FA\u914D\u7F6E\u5931\u8D25:", error);
1943
+ alert("\u5BFC\u51FA\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
1944
+ }
1945
+ };
1946
+ const getPreviewUrl = (id) => {
1947
+ return previewBaseUrl + "?configId=" + id;
1948
+ };
1949
+ const filteredConfigs = configs.filter((config) => {
1950
+ if (!searchTerm) return true;
1951
+ const term = searchTerm.toLowerCase();
1952
+ return config.name.toLowerCase().includes(term) || config.description?.toLowerCase().includes(term);
1953
+ });
1954
+ const totalPages = Math.ceil(filteredConfigs.length / pageSize);
1955
+ const startIndex = (currentPage - 1) * pageSize;
1956
+ const endIndex = startIndex + pageSize;
1957
+ const currentConfigs = filteredConfigs.slice(startIndex, endIndex);
1958
+ if (loading) {
1959
+ return /* @__PURE__ */ React2.createElement("div", { className: clsx("p-8", className) }, /* @__PURE__ */ React2.createElement("div", { className: "text-center" }, /* @__PURE__ */ React2.createElement("div", { className: "inline-block w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ React2.createElement("p", { className: "mt-2 text-gray-600 dark:text-gray-400" }, "\u52A0\u8F7D\u4E2D...")));
1960
+ }
1961
+ return /* @__PURE__ */ React2.createElement("div", { className }, /* @__PURE__ */ React2.createElement("div", { className: "mb-4 flex items-center justify-between gap-4" }, /* @__PURE__ */ React2.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React2.createElement(
1962
+ "input",
1963
+ {
1964
+ type: "text",
1965
+ placeholder: "\u641C\u7D22\u914D\u7F6E...",
1966
+ value: searchTerm,
1967
+ onChange: (e) => {
1968
+ setSearchTerm(e.target.value);
1969
+ setCurrentPage(1);
1970
+ },
1971
+ className: "w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white"
1972
+ }
1973
+ )), /* @__PURE__ */ React2.createElement(
1974
+ "button",
1975
+ {
1976
+ onClick: handleRefresh,
1977
+ className: "p-2 text-gray-600 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-200",
1978
+ title: "\u5237\u65B0"
1979
+ },
1980
+ /* @__PURE__ */ React2.createElement(RefreshCw, { className: "w-5 h-5" })
1981
+ )), currentConfigs.length === 0 ? /* @__PURE__ */ React2.createElement("div", { className: "text-center py-12 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700" }, /* @__PURE__ */ React2.createElement("p", { className: "text-gray-600 dark:text-gray-400" }, searchTerm ? "\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u914D\u7F6E" : "\u8FD8\u6CA1\u6709\u4EFB\u4F55\u914D\u7F6E")) : /* @__PURE__ */ React2.createElement("div", { className: "space-y-3" }, currentConfigs.map((config) => /* @__PURE__ */ React2.createElement(
1982
+ "div",
1983
+ {
1984
+ key: config.id,
1985
+ className: "bg-white dark:bg-gray-800 rounded-lg shadow-sm p-4 border border-gray-200 dark:border-gray-700 hover:shadow-md transition-shadow"
1986
+ },
1987
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-start justify-between gap-4" }, /* @__PURE__ */ React2.createElement(
1988
+ "div",
1989
+ {
1990
+ className: "flex-1 cursor-pointer",
1991
+ onClick: () => handleSelect(config.id)
1992
+ },
1993
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React2.createElement("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white" }, config.name), config.isDefault && /* @__PURE__ */ React2.createElement("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200" }, /* @__PURE__ */ React2.createElement(Star, { className: "w-3 h-3" }), "\u9ED8\u8BA4")),
1994
+ config.description && /* @__PURE__ */ React2.createElement("p", { className: "text-sm text-gray-600 dark:text-gray-400 mb-2" }, config.description),
1995
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-4 text-xs text-gray-500 dark:text-gray-400" }, /* @__PURE__ */ React2.createElement("span", null, "\u7ED3\u679C\u6570: ", config.resultCount), /* @__PURE__ */ React2.createElement("span", null, "\u521B\u5EFA: ", new Date(config.createdAt).toLocaleDateString()), config.updatedAt !== config.createdAt && /* @__PURE__ */ React2.createElement("span", null, "\u66F4\u65B0: ", new Date(config.updatedAt).toLocaleDateString()))
1996
+ ), showActions && /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-1" }, showPreviewLink && /* @__PURE__ */ React2.createElement(
1997
+ "a",
1998
+ {
1999
+ href: getPreviewUrl(config.id),
2000
+ target: "_blank",
2001
+ rel: "noopener noreferrer",
2002
+ className: "p-2 text-gray-600 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400",
2003
+ title: "\u9884\u89C8"
2004
+ },
2005
+ /* @__PURE__ */ React2.createElement(ExternalLink, { className: "w-4 h-4" })
2006
+ ), /* @__PURE__ */ React2.createElement(
2007
+ "button",
2008
+ {
2009
+ onClick: () => handleSetDefault(config.id),
2010
+ disabled: config.isDefault,
2011
+ className: "p-2 text-gray-600 hover:text-yellow-600 disabled:opacity-50 disabled:cursor-not-allowed",
2012
+ title: "\u8BBE\u4E3A\u9ED8\u8BA4"
2013
+ },
2014
+ /* @__PURE__ */ React2.createElement(Star, { className: "w-4 h-4" })
2015
+ ), /* @__PURE__ */ React2.createElement(
2016
+ "button",
2017
+ {
2018
+ onClick: () => handleEdit(config.id),
2019
+ className: "p-2 text-gray-600 hover:text-blue-600",
2020
+ title: "\u7F16\u8F91"
2021
+ },
2022
+ /* @__PURE__ */ React2.createElement(Edit, { className: "w-4 h-4" })
2023
+ ), /* @__PURE__ */ React2.createElement(
2024
+ "button",
2025
+ {
2026
+ onClick: () => handleDuplicate(config.id),
2027
+ className: "p-2 text-gray-600 hover:text-green-600",
2028
+ title: "\u590D\u5236"
2029
+ },
2030
+ /* @__PURE__ */ React2.createElement(Copy, { className: "w-4 h-4" })
2031
+ ), /* @__PURE__ */ React2.createElement(
2032
+ "button",
2033
+ {
2034
+ onClick: () => handleExport(config.id),
2035
+ className: "p-2 text-gray-600 hover:text-purple-600",
2036
+ title: "\u5BFC\u51FA"
2037
+ },
2038
+ /* @__PURE__ */ React2.createElement(Download, { className: "w-4 h-4" })
2039
+ ), /* @__PURE__ */ React2.createElement(
2040
+ "button",
2041
+ {
2042
+ onClick: () => handleDelete(config.id),
2043
+ className: "p-2 text-gray-600 hover:text-red-600",
2044
+ title: "\u5220\u9664"
2045
+ },
2046
+ /* @__PURE__ */ React2.createElement(Trash2, { className: "w-4 h-4" })
2047
+ )))
2048
+ ))), totalPages > 1 && /* @__PURE__ */ React2.createElement("div", { className: "mt-4 flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("p", { className: "text-sm text-gray-600 dark:text-gray-400" }, "\u663E\u793A ", startIndex + 1, " - ", Math.min(endIndex, filteredConfigs.length), " \u5171", " ", filteredConfigs.length, " \u4E2A\u914D\u7F6E"), /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React2.createElement(
2049
+ "button",
2050
+ {
2051
+ onClick: () => setCurrentPage((p) => Math.max(1, p - 1)),
2052
+ disabled: currentPage === 1,
2053
+ className: "px-3 py-1 border border-gray-300 dark:border-gray-600 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-50 dark:hover:bg-gray-700"
2054
+ },
2055
+ "\u4E0A\u4E00\u9875"
2056
+ ), /* @__PURE__ */ React2.createElement("span", { className: "text-sm text-gray-600 dark:text-gray-400" }, currentPage, " / ", totalPages), /* @__PURE__ */ React2.createElement(
2057
+ "button",
2058
+ {
2059
+ onClick: () => setCurrentPage((p) => Math.min(totalPages, p + 1)),
2060
+ disabled: currentPage === totalPages,
2061
+ className: "px-3 py-1 border border-gray-300 dark:border-gray-600 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-50 dark:hover:bg-gray-700"
2062
+ },
2063
+ "\u4E0B\u4E00\u9875"
2064
+ ))));
2065
+ };
2066
+
2067
+ // src/testYourself/server/index.ts
2068
+ init_ConfigService();
2069
+ var testYourselfConfigs = pgTable(
2070
+ "test_yourself_configs",
2071
+ {
2072
+ // ========== 主键 ==========
2073
+ /** 配置唯一ID */
2074
+ id: uuid("id").primaryKey().defaultRandom(),
2075
+ // ========== 基本信息 ==========
2076
+ /** 配置名称 */
2077
+ name: varchar("name", { length: 255 }).notNull(),
2078
+ /** 配置描述 */
2079
+ description: text("description"),
2080
+ /** 配置标签(用于分类和搜索) */
2081
+ tags: json("tags").$type().default([]),
2082
+ // ========== 配置数据 ==========
2083
+ /** 测试配置内容(JSON格式) */
2084
+ config: json("config").$type().notNull(),
2085
+ /** 结果数量(冗余字段,便于查询) */
2086
+ resultCount: integer("result_count").notNull().default(0),
2087
+ // ========== 状态字段 ==========
2088
+ /** 是否为默认配置 */
2089
+ isDefault: boolean("is_default").notNull().default(false),
2090
+ /** 是否已发布(草稿/发布) */
2091
+ isPublished: boolean("is_published").notNull().default(true),
2092
+ /** 是否已归档 */
2093
+ isArchived: boolean("is_archived").notNull().default(false),
2094
+ /** 是否已删除(软删除) */
2095
+ isDeleted: boolean("is_deleted").notNull().default(false),
2096
+ // ========== 权限和所有权 ==========
2097
+ /** 创建者ID */
2098
+ createdBy: varchar("created_by", { length: 255 }).notNull(),
2099
+ /** 最后更新者ID */
2100
+ updatedBy: varchar("updated_by", { length: 255 }),
2101
+ /** 所属组织/租户ID(多租户支持) */
2102
+ organizationId: varchar("organization_id", { length: 255 }),
2103
+ // ========== 统计信息 ==========
2104
+ /** 使用次数 */
2105
+ usageCount: integer("usage_count").notNull().default(0),
2106
+ /** 最后使用时间 */
2107
+ lastUsedAt: timestamp("last_used_at"),
2108
+ /** 浏览次数 */
2109
+ viewCount: integer("view_count").notNull().default(0),
2110
+ // ========== 版本控制 ==========
2111
+ /** 配置版本号 */
2112
+ version: integer("version").notNull().default(1),
2113
+ /** 父配置ID(用于版本追踪) */
2114
+ parentId: uuid("parent_id"),
2115
+ // ========== 自定义字段 ==========
2116
+ /** 自定义元数据(扩展字段) */
2117
+ metadata: json("metadata").$type(),
2118
+ /** 配置来源(web/api/import) */
2119
+ source: varchar("source", { length: 50 }),
2120
+ // ========== 时间戳 ==========
2121
+ /** 创建时间 */
2122
+ createdAt: timestamp("created_at").defaultNow().notNull(),
2123
+ /** 更新时间 */
2124
+ updatedAt: timestamp("updated_at").defaultNow().notNull(),
2125
+ /** 发布时间 */
2126
+ publishedAt: timestamp("published_at"),
2127
+ /** 归档时间 */
2128
+ archivedAt: timestamp("archived_at"),
2129
+ /** 删除时间 */
2130
+ deletedAt: timestamp("deleted_at")
2131
+ },
2132
+ (table) => ({
2133
+ // ========== 索引设计 ==========
2134
+ /** 名称搜索索引 */
2135
+ nameIndex: index("test_configs_name_idx").on(table.name),
2136
+ /** 创建者索引 */
2137
+ createdByIndex: index("test_configs_created_by_idx").on(table.createdBy),
2138
+ /** 组织索引(多租户) */
2139
+ organizationIndex: index("test_configs_organization_idx").on(table.organizationId),
2140
+ /** 默认配置索引 */
2141
+ isDefaultIndex: index("test_configs_is_default_idx").on(table.isDefault),
2142
+ /** 发布状态索引 */
2143
+ isPublishedIndex: index("test_configs_is_published_idx").on(table.isPublished),
2144
+ /** 删除状态索引 */
2145
+ isDeletedIndex: index("test_configs_is_deleted_idx").on(table.isDeleted),
2146
+ /** 创建时间索引 */
2147
+ createdAtIndex: index("test_configs_created_at_idx").on(table.createdAt),
2148
+ /** 最后使用时间索引 */
2149
+ lastUsedAtIndex: index("test_configs_last_used_at_idx").on(table.lastUsedAt),
2150
+ /** 组合索引:组织+删除状态+发布状态 */
2151
+ orgDeletedPublishedIndex: index("test_configs_org_deleted_published_idx").on(
2152
+ table.organizationId,
2153
+ table.isDeleted,
2154
+ table.isPublished
2155
+ ),
2156
+ /** 组合索引:创建者+删除状态 */
2157
+ createdByDeletedIndex: index("test_configs_created_by_deleted_idx").on(
2158
+ table.createdBy,
2159
+ table.isDeleted
2160
+ )
2161
+ })
2162
+ );
2163
+ var testYourselfConfigUsage = pgTable(
2164
+ "test_yourself_config_usage",
2165
+ {
2166
+ /** 记录ID */
2167
+ id: uuid("id").primaryKey().defaultRandom(),
2168
+ /** 配置ID */
2169
+ configId: uuid("config_id").references(() => testYourselfConfigs.id, { onDelete: "cascade" }).notNull(),
2170
+ /** 用户ID */
2171
+ userId: varchar("user_id", { length: 255 }),
2172
+ /** 设备指纹 */
2173
+ fingerprint: text("fingerprint"),
2174
+ /** 测试结果ID */
2175
+ resultId: varchar("result_id", { length: 255 }),
2176
+ /** IP地址 */
2177
+ ipAddress: varchar("ip_address", { length: 45 }),
2178
+ /** User Agent */
2179
+ userAgent: text("user_agent"),
2180
+ /** 来源页面 */
2181
+ referer: text("referer"),
2182
+ /** 使用时间 */
2183
+ usedAt: timestamp("used_at").defaultNow().notNull(),
2184
+ /** 完成时间(毫秒) */
2185
+ completionTime: integer("completion_time"),
2186
+ /** 额外数据 */
2187
+ metadata: json("metadata").$type()
2188
+ },
2189
+ (table) => ({
2190
+ /** 配置ID索引 */
2191
+ configIndex: index("test_usage_config_idx").on(table.configId),
2192
+ /** 用户ID索引 */
2193
+ userIndex: index("test_usage_user_idx").on(table.userId),
2194
+ /** 使用时间索引 */
2195
+ usedAtIndex: index("test_usage_used_at_idx").on(table.usedAt),
2196
+ /** 指纹索引 */
2197
+ fingerprintIndex: index("test_usage_fingerprint_idx").on(table.fingerprint)
2198
+ })
2199
+ );
2200
+ var testYourselfConfigShares = pgTable(
2201
+ "test_yourself_config_shares",
2202
+ {
2203
+ /** 分享ID */
2204
+ id: uuid("id").primaryKey().defaultRandom(),
2205
+ /** 分享代码(短链接标识) */
2206
+ shareCode: varchar("share_code", { length: 20 }).notNull().unique(),
2207
+ /** 配置ID */
2208
+ configId: uuid("config_id").references(() => testYourselfConfigs.id, { onDelete: "cascade" }).notNull(),
2209
+ /** 分享标题 */
2210
+ title: varchar("title", { length: 255 }),
2211
+ /** 分享描述 */
2212
+ description: text("description"),
2213
+ /** 访问密码 */
2214
+ password: varchar("password", { length: 100 }),
2215
+ /** 最大访问次数 */
2216
+ maxAccess: integer("max_access"),
2217
+ /** 当前访问次数 */
2218
+ accessCount: integer("access_count").notNull().default(0),
2219
+ /** 是否启用 */
2220
+ isActive: boolean("is_active").notNull().default(true),
2221
+ /** 过期时间 */
2222
+ expiresAt: timestamp("expires_at"),
2223
+ /** 创建者ID */
2224
+ createdBy: varchar("created_by", { length: 255 }).notNull(),
2225
+ /** 创建时间 */
2226
+ createdAt: timestamp("created_at").defaultNow().notNull(),
2227
+ /** 更新时间 */
2228
+ updatedAt: timestamp("updated_at").defaultNow().notNull()
2229
+ },
2230
+ (table) => ({
2231
+ /** 分享代码索引 */
2232
+ shareCodeIndex: index("test_shares_share_code_idx").on(table.shareCode),
2233
+ /** 配置ID索引 */
2234
+ configIndex: index("test_shares_config_idx").on(table.configId),
2235
+ /** 创建者索引 */
2236
+ createdByIndex: index("test_shares_created_by_idx").on(table.createdBy),
2237
+ /** 活跃状态索引 */
2238
+ isActiveIndex: index("test_shares_is_active_idx").on(table.isActive)
2239
+ })
2240
+ );
2241
+
2242
+ // src/testYourself/server/DatabaseConfigAdapter.ts
2243
+ var DatabaseConfigAdapter = class {
2244
+ constructor(options) {
2245
+ this.db = options.db;
2246
+ this.userId = options.userId || "system";
2247
+ this.organizationId = options.organizationId;
2248
+ this.softDelete = options.softDelete !== false;
2249
+ }
2250
+ /**
2251
+ * 设置当前用户ID
2252
+ */
2253
+ setUserId(userId) {
2254
+ this.userId = userId;
2255
+ }
2256
+ /**
2257
+ * 设置组织ID
2258
+ */
2259
+ setOrganizationId(organizationId) {
2260
+ this.organizationId = organizationId;
2261
+ }
2262
+ /**
2263
+ * 将数据库记录转换为 SavedConfig
2264
+ */
2265
+ toSavedConfig(dbConfig) {
2266
+ return {
2267
+ id: dbConfig.id,
2268
+ name: dbConfig.name,
2269
+ description: dbConfig.description || void 0,
2270
+ config: dbConfig.config,
2271
+ createdAt: dbConfig.createdAt.getTime(),
2272
+ updatedAt: dbConfig.updatedAt.getTime(),
2273
+ isDefault: dbConfig.isDefault
2274
+ };
2275
+ }
2276
+ /**
2277
+ * 保存配置
2278
+ */
2279
+ async saveConfig(config) {
2280
+ try {
2281
+ const existing = await this.db.select().from(testYourselfConfigs).where(eq(testYourselfConfigs.id, config.id)).limit(1);
2282
+ const resultCount = config.config.results?.length || 0;
2283
+ if (existing && existing.length > 0) {
2284
+ await this.db.update(testYourselfConfigs).set({
2285
+ name: config.name,
2286
+ description: config.description || null,
2287
+ config: config.config,
2288
+ resultCount,
2289
+ isDefault: config.isDefault || false,
2290
+ updatedBy: this.userId,
2291
+ updatedAt: /* @__PURE__ */ new Date(),
2292
+ version: sql`${testYourselfConfigs.version} + 1`
2293
+ // 版本号递增
2294
+ }).where(eq(testYourselfConfigs.id, config.id));
2295
+ console.log("\u2705 [DatabaseAdapter] \u914D\u7F6E\u5DF2\u66F4\u65B0:", config.id);
2296
+ } else {
2297
+ await this.db.insert(testYourselfConfigs).values({
2298
+ id: config.id,
2299
+ name: config.name,
2300
+ description: config.description || null,
2301
+ config: config.config,
2302
+ resultCount,
2303
+ isDefault: config.isDefault || false,
2304
+ createdBy: this.userId,
2305
+ organizationId: this.organizationId,
2306
+ createdAt: new Date(config.createdAt),
2307
+ updatedAt: new Date(config.updatedAt)
2308
+ });
2309
+ console.log("\u2705 [DatabaseAdapter] \u914D\u7F6E\u5DF2\u521B\u5EFA:", config.id);
2310
+ }
2311
+ } catch (error) {
2312
+ console.error("\u274C [DatabaseAdapter] \u4FDD\u5B58\u914D\u7F6E\u5931\u8D25:", error);
2313
+ throw error;
2314
+ }
2315
+ }
2316
+ /**
2317
+ * 获取配置
2318
+ */
2319
+ async getConfig(id) {
2320
+ try {
2321
+ const conditions = [eq(testYourselfConfigs.id, id)];
2322
+ if (this.softDelete) {
2323
+ conditions.push(eq(testYourselfConfigs.isDeleted, false));
2324
+ }
2325
+ const result = await this.db.select().from(testYourselfConfigs).where(and(...conditions)).limit(1);
2326
+ if (!result || result.length === 0) {
2327
+ return null;
2328
+ }
2329
+ return this.toSavedConfig(result[0]);
2330
+ } catch (error) {
2331
+ console.error("\u274C [DatabaseAdapter] \u83B7\u53D6\u914D\u7F6E\u5931\u8D25:", error);
2332
+ throw error;
2333
+ }
2334
+ }
2335
+ /**
2336
+ * 获取所有配置列表
2337
+ */
2338
+ async getAllConfigs() {
2339
+ try {
2340
+ const conditions = [];
2341
+ if (this.softDelete) {
2342
+ conditions.push(eq(testYourselfConfigs.isDeleted, false));
2343
+ }
2344
+ if (this.organizationId) {
2345
+ conditions.push(
2346
+ or(
2347
+ eq(testYourselfConfigs.organizationId, this.organizationId),
2348
+ sql`${testYourselfConfigs.organizationId} IS NULL`
2349
+ // 包含全局配置
2350
+ )
2351
+ );
2352
+ }
2353
+ const result = await this.db.select().from(testYourselfConfigs).where(conditions.length > 0 ? and(...conditions) : void 0).orderBy(desc(testYourselfConfigs.createdAt));
2354
+ return result.map((config) => this.toSavedConfig(config));
2355
+ } catch (error) {
2356
+ console.error("\u274C [DatabaseAdapter] \u83B7\u53D6\u914D\u7F6E\u5217\u8868\u5931\u8D25:", error);
2357
+ throw error;
2358
+ }
2359
+ }
2360
+ /**
2361
+ * 删除配置
2362
+ */
2363
+ async deleteConfig(id) {
2364
+ try {
2365
+ if (this.softDelete) {
2366
+ await this.db.update(testYourselfConfigs).set({
2367
+ isDeleted: true,
2368
+ deletedAt: /* @__PURE__ */ new Date(),
2369
+ updatedBy: this.userId,
2370
+ updatedAt: /* @__PURE__ */ new Date()
2371
+ }).where(eq(testYourselfConfigs.id, id));
2372
+ console.log("\u{1F5D1}\uFE0F [DatabaseAdapter] \u914D\u7F6E\u5DF2\u8F6F\u5220\u9664:", id);
2373
+ } else {
2374
+ await this.db.delete(testYourselfConfigs).where(eq(testYourselfConfigs.id, id));
2375
+ console.log("\u{1F5D1}\uFE0F [DatabaseAdapter] \u914D\u7F6E\u5DF2\u786C\u5220\u9664:", id);
2376
+ }
2377
+ } catch (error) {
2378
+ console.error("\u274C [DatabaseAdapter] \u5220\u9664\u914D\u7F6E\u5931\u8D25:", error);
2379
+ throw error;
2380
+ }
2381
+ }
2382
+ /**
2383
+ * 更新配置
2384
+ */
2385
+ async updateConfig(id, config) {
2386
+ try {
2387
+ const resultCount = config.config.results?.length || 0;
2388
+ await this.db.update(testYourselfConfigs).set({
2389
+ name: config.name,
2390
+ description: config.description || null,
2391
+ config: config.config,
2392
+ resultCount,
2393
+ isDefault: config.isDefault || false,
2394
+ updatedBy: this.userId,
2395
+ updatedAt: new Date(config.updatedAt),
2396
+ version: sql`${testYourselfConfigs.version} + 1`
2397
+ }).where(eq(testYourselfConfigs.id, id));
2398
+ console.log("\u2705 [DatabaseAdapter] \u914D\u7F6E\u5DF2\u66F4\u65B0:", id);
2399
+ } catch (error) {
2400
+ console.error("\u274C [DatabaseAdapter] \u66F4\u65B0\u914D\u7F6E\u5931\u8D25:", error);
2401
+ throw error;
2402
+ }
2403
+ }
2404
+ /**
2405
+ * 设置默认配置
2406
+ */
2407
+ async setDefaultConfig(id) {
2408
+ try {
2409
+ await this.db.transaction(async (tx) => {
2410
+ const conditions = [eq(testYourselfConfigs.isDefault, true)];
2411
+ if (this.organizationId) {
2412
+ conditions.push(
2413
+ eq(testYourselfConfigs.organizationId, this.organizationId)
2414
+ );
2415
+ }
2416
+ await tx.update(testYourselfConfigs).set({
2417
+ isDefault: false,
2418
+ updatedBy: this.userId,
2419
+ updatedAt: /* @__PURE__ */ new Date()
2420
+ }).where(and(...conditions));
2421
+ await tx.update(testYourselfConfigs).set({
2422
+ isDefault: true,
2423
+ updatedBy: this.userId,
2424
+ updatedAt: /* @__PURE__ */ new Date()
2425
+ }).where(eq(testYourselfConfigs.id, id));
2426
+ });
2427
+ console.log("\u2705 [DatabaseAdapter] \u9ED8\u8BA4\u914D\u7F6E\u5DF2\u8BBE\u7F6E:", id);
2428
+ } catch (error) {
2429
+ console.error("\u274C [DatabaseAdapter] \u8BBE\u7F6E\u9ED8\u8BA4\u914D\u7F6E\u5931\u8D25:", error);
2430
+ throw error;
2431
+ }
2432
+ }
2433
+ /**
2434
+ * 获取默认配置
2435
+ */
2436
+ async getDefaultConfig() {
2437
+ try {
2438
+ const conditions = [eq(testYourselfConfigs.isDefault, true)];
2439
+ if (this.softDelete) {
2440
+ conditions.push(eq(testYourselfConfigs.isDeleted, false));
2441
+ }
2442
+ if (this.organizationId) {
2443
+ conditions.push(
2444
+ eq(testYourselfConfigs.organizationId, this.organizationId)
2445
+ );
2446
+ }
2447
+ const result = await this.db.select().from(testYourselfConfigs).where(and(...conditions)).limit(1);
2448
+ if (!result || result.length === 0) {
2449
+ return null;
2450
+ }
2451
+ return this.toSavedConfig(result[0]);
2452
+ } catch (error) {
2453
+ console.error("\u274C [DatabaseAdapter] \u83B7\u53D6\u9ED8\u8BA4\u914D\u7F6E\u5931\u8D25:", error);
2454
+ throw error;
2455
+ }
2456
+ }
2457
+ /**
2458
+ * 恢复已删除的配置(软删除时可用)
2459
+ */
2460
+ async restoreConfig(id) {
2461
+ if (!this.softDelete) {
2462
+ throw new Error("\u6062\u590D\u529F\u80FD\u4EC5\u5728\u542F\u7528\u8F6F\u5220\u9664\u65F6\u53EF\u7528");
2463
+ }
2464
+ try {
2465
+ await this.db.update(testYourselfConfigs).set({
2466
+ isDeleted: false,
2467
+ deletedAt: null,
2468
+ updatedBy: this.userId,
2469
+ updatedAt: /* @__PURE__ */ new Date()
2470
+ }).where(eq(testYourselfConfigs.id, id));
2471
+ console.log("\u267B\uFE0F [DatabaseAdapter] \u914D\u7F6E\u5DF2\u6062\u590D:", id);
2472
+ } catch (error) {
2473
+ console.error("\u274C [DatabaseAdapter] \u6062\u590D\u914D\u7F6E\u5931\u8D25:", error);
2474
+ throw error;
2475
+ }
2476
+ }
2477
+ /**
2478
+ * 归档配置
2479
+ */
2480
+ async archiveConfig(id) {
2481
+ try {
2482
+ await this.db.update(testYourselfConfigs).set({
2483
+ isArchived: true,
2484
+ archivedAt: /* @__PURE__ */ new Date(),
2485
+ updatedBy: this.userId,
2486
+ updatedAt: /* @__PURE__ */ new Date()
2487
+ }).where(eq(testYourselfConfigs.id, id));
2488
+ console.log("\u{1F4E6} [DatabaseAdapter] \u914D\u7F6E\u5DF2\u5F52\u6863:", id);
2489
+ } catch (error) {
2490
+ console.error("\u274C [DatabaseAdapter] \u5F52\u6863\u914D\u7F6E\u5931\u8D25:", error);
2491
+ throw error;
2492
+ }
2493
+ }
2494
+ /**
2495
+ * 取消归档
2496
+ */
2497
+ async unarchiveConfig(id) {
2498
+ try {
2499
+ await this.db.update(testYourselfConfigs).set({
2500
+ isArchived: false,
2501
+ archivedAt: null,
2502
+ updatedBy: this.userId,
2503
+ updatedAt: /* @__PURE__ */ new Date()
2504
+ }).where(eq(testYourselfConfigs.id, id));
2505
+ console.log("\u{1F4C2} [DatabaseAdapter] \u914D\u7F6E\u5DF2\u53D6\u6D88\u5F52\u6863:", id);
2506
+ } catch (error) {
2507
+ console.error("\u274C [DatabaseAdapter] \u53D6\u6D88\u5F52\u6863\u5931\u8D25:", error);
2508
+ throw error;
2509
+ }
2510
+ }
2511
+ /**
2512
+ * 记录使用次数
2513
+ */
2514
+ async incrementUsageCount(id) {
2515
+ try {
2516
+ await this.db.update(testYourselfConfigs).set({
2517
+ usageCount: sql`${testYourselfConfigs.usageCount} + 1`,
2518
+ lastUsedAt: /* @__PURE__ */ new Date()
2519
+ }).where(eq(testYourselfConfigs.id, id));
2520
+ } catch (error) {
2521
+ console.error("\u274C [DatabaseAdapter] \u66F4\u65B0\u4F7F\u7528\u6B21\u6570\u5931\u8D25:", error);
2522
+ }
2523
+ }
2524
+ };
2525
+ function createDatabaseConfigAdapter(options) {
2526
+ return new DatabaseConfigAdapter(options);
2527
+ }
2528
+
2529
+ export { ConfigList, ConfigManager, ConfigService, DEFAULT_RESULTS, DatabaseConfigAdapter, TestYourself, createConfigService, createDatabaseConfigAdapter, generateDeviceHash, getDefaultConfigService, getDeviceFingerprint, selectResultIndex, testYourselfConfigShares, testYourselfConfigUsage, testYourselfConfigs, tryGetIPAddress };
2530
+ //# sourceMappingURL=index.mjs.map
2531
+ //# sourceMappingURL=index.mjs.map