sa2kit 1.6.91 → 1.6.96

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 (117) hide show
  1. package/dist/client-BlkUL2To.d.ts +26 -0
  2. package/dist/client-DpMIhrlS.d.mts +26 -0
  3. package/dist/huarongdao/index.d.mts +8 -0
  4. package/dist/huarongdao/index.d.ts +8 -0
  5. package/dist/huarongdao/index.js +360 -0
  6. package/dist/huarongdao/index.js.map +1 -0
  7. package/dist/huarongdao/index.mjs +338 -0
  8. package/dist/huarongdao/index.mjs.map +1 -0
  9. package/dist/huarongdao/logic/index.d.mts +11 -0
  10. package/dist/huarongdao/logic/index.d.ts +11 -0
  11. package/dist/huarongdao/logic/index.js +89 -0
  12. package/dist/huarongdao/logic/index.js.map +1 -0
  13. package/dist/huarongdao/logic/index.mjs +81 -0
  14. package/dist/huarongdao/logic/index.mjs.map +1 -0
  15. package/dist/huarongdao/routes/index.d.mts +38 -0
  16. package/dist/huarongdao/routes/index.d.ts +38 -0
  17. package/dist/huarongdao/routes/index.js +114 -0
  18. package/dist/huarongdao/routes/index.js.map +1 -0
  19. package/dist/huarongdao/routes/index.mjs +108 -0
  20. package/dist/huarongdao/routes/index.mjs.map +1 -0
  21. package/dist/huarongdao/server/index.d.mts +14 -0
  22. package/dist/huarongdao/server/index.d.ts +14 -0
  23. package/dist/huarongdao/server/index.js +60 -0
  24. package/dist/huarongdao/server/index.js.map +1 -0
  25. package/dist/huarongdao/server/index.mjs +57 -0
  26. package/dist/huarongdao/server/index.mjs.map +1 -0
  27. package/dist/huarongdao/service/index.d.mts +31 -0
  28. package/dist/huarongdao/service/index.d.ts +31 -0
  29. package/dist/huarongdao/service/index.js +45 -0
  30. package/dist/huarongdao/service/index.js.map +1 -0
  31. package/dist/huarongdao/service/index.mjs +42 -0
  32. package/dist/huarongdao/service/index.mjs.map +1 -0
  33. package/dist/huarongdao/types/index.d.mts +46 -0
  34. package/dist/huarongdao/types/index.d.ts +46 -0
  35. package/dist/huarongdao/types/index.js +4 -0
  36. package/dist/huarongdao/types/index.js.map +1 -0
  37. package/dist/huarongdao/types/index.mjs +3 -0
  38. package/dist/huarongdao/types/index.mjs.map +1 -0
  39. package/dist/huarongdao/ui/web/index.d.mts +3 -0
  40. package/dist/huarongdao/ui/web/index.d.ts +3 -0
  41. package/dist/huarongdao/ui/web/index.js +237 -0
  42. package/dist/huarongdao/ui/web/index.js.map +1 -0
  43. package/dist/huarongdao/ui/web/index.mjs +229 -0
  44. package/dist/huarongdao/ui/web/index.mjs.map +1 -0
  45. package/dist/index-B48rcsqv.d.ts +27 -0
  46. package/dist/index-BNqJdwX4.d.ts +37 -0
  47. package/dist/index-C7yh6b5Q.d.mts +17 -0
  48. package/dist/index-CDapUIT5.d.mts +51 -0
  49. package/dist/index-Cv9jlnNz.d.ts +17 -0
  50. package/dist/index-D3UbkUai.d.ts +51 -0
  51. package/dist/index-DOtQI_mz.d.mts +37 -0
  52. package/dist/index-Da2X78GE.d.mts +27 -0
  53. package/dist/index.d.mts +18 -0
  54. package/dist/index.d.ts +18 -0
  55. package/dist/index.js +1707 -79
  56. package/dist/index.js.map +1 -1
  57. package/dist/index.mjs +1675 -82
  58. package/dist/index.mjs.map +1 -1
  59. package/dist/mikuContest/index.d.mts +13 -0
  60. package/dist/mikuContest/index.d.ts +13 -0
  61. package/dist/mikuContest/index.js +1310 -0
  62. package/dist/mikuContest/index.js.map +1 -0
  63. package/dist/mikuContest/index.mjs +1253 -0
  64. package/dist/mikuContest/index.mjs.map +1 -0
  65. package/dist/mikuContest/logic/index.d.mts +32 -0
  66. package/dist/mikuContest/logic/index.d.ts +32 -0
  67. package/dist/mikuContest/logic/index.js +511 -0
  68. package/dist/mikuContest/logic/index.js.map +1 -0
  69. package/dist/mikuContest/logic/index.mjs +483 -0
  70. package/dist/mikuContest/logic/index.mjs.map +1 -0
  71. package/dist/mikuContest/routes/index.d.mts +80 -0
  72. package/dist/mikuContest/routes/index.d.ts +80 -0
  73. package/dist/mikuContest/routes/index.js +821 -0
  74. package/dist/mikuContest/routes/index.js.map +1 -0
  75. package/dist/mikuContest/routes/index.mjs +791 -0
  76. package/dist/mikuContest/routes/index.mjs.map +1 -0
  77. package/dist/mikuContest/server/index.d.mts +766 -0
  78. package/dist/mikuContest/server/index.d.ts +766 -0
  79. package/dist/mikuContest/server/index.js +705 -0
  80. package/dist/mikuContest/server/index.js.map +1 -0
  81. package/dist/mikuContest/server/index.mjs +672 -0
  82. package/dist/mikuContest/server/index.mjs.map +1 -0
  83. package/dist/mikuContest/service/index.d.mts +30 -0
  84. package/dist/mikuContest/service/index.d.ts +30 -0
  85. package/dist/mikuContest/service/index.js +139 -0
  86. package/dist/mikuContest/service/index.js.map +1 -0
  87. package/dist/mikuContest/service/index.mjs +135 -0
  88. package/dist/mikuContest/service/index.mjs.map +1 -0
  89. package/dist/mikuContest/types/index.d.mts +179 -0
  90. package/dist/mikuContest/types/index.d.ts +179 -0
  91. package/dist/mikuContest/types/index.js +4 -0
  92. package/dist/mikuContest/types/index.js.map +1 -0
  93. package/dist/mikuContest/types/index.mjs +3 -0
  94. package/dist/mikuContest/types/index.mjs.map +1 -0
  95. package/dist/mikuContest/ui/miniapp/index.d.mts +3 -0
  96. package/dist/mikuContest/ui/miniapp/index.d.ts +3 -0
  97. package/dist/mikuContest/ui/miniapp/index.js +566 -0
  98. package/dist/mikuContest/ui/miniapp/index.js.map +1 -0
  99. package/dist/mikuContest/ui/miniapp/index.mjs +540 -0
  100. package/dist/mikuContest/ui/miniapp/index.mjs.map +1 -0
  101. package/dist/mikuContest/ui/web/index.d.mts +4 -0
  102. package/dist/mikuContest/ui/web/index.d.ts +4 -0
  103. package/dist/mikuContest/ui/web/index.js +353 -0
  104. package/dist/mikuContest/ui/web/index.js.map +1 -0
  105. package/dist/mikuContest/ui/web/index.mjs +343 -0
  106. package/dist/mikuContest/ui/web/index.mjs.map +1 -0
  107. package/dist/qqbot/server/index.d.mts +126 -1
  108. package/dist/qqbot/server/index.d.ts +126 -1
  109. package/dist/qqbot/server/index.js +250 -0
  110. package/dist/qqbot/server/index.js.map +1 -1
  111. package/dist/qqbot/server/index.mjs +246 -1
  112. package/dist/qqbot/server/index.mjs.map +1 -1
  113. package/dist/service-D7DM1wW-.d.ts +38 -0
  114. package/dist/service-DPr2rlvH.d.mts +38 -0
  115. package/dist/types-BS7Xz09b.d.mts +14 -0
  116. package/dist/types-k4koMp4m.d.ts +14 -0
  117. package/package.json +76 -1
package/dist/index.js CHANGED
@@ -18,6 +18,8 @@ var crypto = require('crypto');
18
18
  require('bcryptjs');
19
19
  require('jsonwebtoken');
20
20
  var THREE2 = require('three');
21
+ var XLSX = require('xlsx');
22
+ var server = require('next/server');
21
23
 
22
24
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
23
25
 
@@ -42,13 +44,19 @@ function _interopNamespace(e) {
42
44
  var React69__namespace = /*#__PURE__*/_interopNamespace(React69);
43
45
  var Link__default = /*#__PURE__*/_interopDefault(Link);
44
46
  var THREE2__namespace = /*#__PURE__*/_interopNamespace(THREE2);
47
+ var XLSX__namespace = /*#__PURE__*/_interopNamespace(XLSX);
45
48
 
49
+ var __defProp = Object.defineProperty;
46
50
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
47
51
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
48
52
  }) : x)(function(x) {
49
53
  if (typeof require !== "undefined") return require.apply(this, arguments);
50
54
  throw Error('Dynamic require of "' + x + '" is not supported');
51
55
  });
56
+ var __export = (target, all) => {
57
+ for (var name in all)
58
+ __defProp(target, name, { get: all[name], enumerable: true });
59
+ };
52
60
 
53
61
  // src/logger/types.ts
54
62
  var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
@@ -76,10 +84,10 @@ var ConsoleLoggerAdapter = class {
76
84
  };
77
85
  }
78
86
  log(entry) {
79
- const { level, message, timestamp: timestamp5, data, context, error } = entry;
87
+ const { level, message, timestamp: timestamp6, data, context, error } = entry;
80
88
  let logMessage = "";
81
- if (timestamp5) {
82
- logMessage += "[" + this.formatTimestamp(timestamp5) + "] ";
89
+ if (timestamp6) {
90
+ logMessage += "[" + this.formatTimestamp(timestamp6) + "] ";
83
91
  }
84
92
  const levelName = this.getLevelName(level);
85
93
  logMessage += levelName + ": ";
@@ -572,7 +580,7 @@ var useSentimentAnalysis = (options = {}) => {
572
580
  result: null
573
581
  });
574
582
  const pipelineRef = React69.useRef(null);
575
- const analyze = React69.useCallback(async (text5) => {
583
+ const analyze = React69.useCallback(async (text6) => {
576
584
  setState((prev) => ({
577
585
  ...prev,
578
586
  isProcessing: true,
@@ -619,12 +627,12 @@ var useSentimentAnalysis = (options = {}) => {
619
627
  pipelineRef.current = await pipeline("sentiment-analysis", options.model || defaultModel);
620
628
  }
621
629
  setState((prev) => ({ ...prev, status: "analyzing" }));
622
- const output = await pipelineRef.current(text5);
630
+ const output = await pipelineRef.current(text6);
623
631
  const resultData = output[0];
624
632
  const label = resultData.label.toLowerCase();
625
633
  let sentiment = "neutral";
626
634
  const negativeKeywords = ["\u7D2F", "\u60E8", "\u7EDD\u671B", "\u96BE\u53D7", "\u4F24\u5FC3", "\u5DEE", "\u574F", "\u7CDF", "\u4E0D\u884C"];
627
- const hasNegativeKeyword = negativeKeywords.some((k) => text5.includes(k));
635
+ const hasNegativeKeyword = negativeKeywords.some((k) => text6.includes(k));
628
636
  if (label.includes("positive") && !hasNegativeKeyword) {
629
637
  sentiment = "positive";
630
638
  } else if (label.includes("negative") || label.includes("0") || hasNegativeKeyword) {
@@ -656,12 +664,12 @@ var SentimentAnalyzer = ({
656
664
  className = "",
657
665
  placeholder = "\u8F93\u5165\u4E00\u6BB5\u4E2D\u6587\u6216\u82F1\u6587\uFF0C\u5206\u6790\u5176\u60C5\u611F\u503E\u5411..."
658
666
  }) => {
659
- const [text5, setText] = React69.useState("");
667
+ const [text6, setText] = React69.useState("");
660
668
  const { analyze, isProcessing, status, result, error } = useSentimentAnalysis();
661
669
  const handleAnalyze = async () => {
662
- if (!text5.trim() || isProcessing) return;
670
+ if (!text6.trim() || isProcessing) return;
663
671
  try {
664
- const res = await analyze(text5);
672
+ const res = await analyze(text6);
665
673
  onResult?.(res);
666
674
  } catch (err) {
667
675
  console.error("Sentiment Analysis Error:", err);
@@ -692,7 +700,7 @@ var SentimentAnalyzer = ({
692
700
  return /* @__PURE__ */ React69__namespace.default.createElement("div", { className: clsx.clsx("p-6 border rounded-xl bg-white dark:bg-gray-800 shadow-sm", className) }, /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "flex items-center gap-2 mb-4 text-gray-700 dark:text-gray-300 font-medium" }, /* @__PURE__ */ React69__namespace.default.createElement(lucideReact.MessageSquare, { size: 20 }), /* @__PURE__ */ React69__namespace.default.createElement("span", null, "\u6587\u672C\u60C5\u611F\u5206\u6790")), /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "relative" }, /* @__PURE__ */ React69__namespace.default.createElement(
693
701
  "textarea",
694
702
  {
695
- value: text5,
703
+ value: text6,
696
704
  onChange: (e) => setText(e.target.value),
697
705
  placeholder,
698
706
  className: "w-full h-32 p-4 bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all outline-none resize-none text-gray-800 dark:text-gray-200",
@@ -702,7 +710,7 @@ var SentimentAnalyzer = ({
702
710
  "button",
703
711
  {
704
712
  onClick: handleAnalyze,
705
- disabled: !text5.trim() || isProcessing,
713
+ disabled: !text6.trim() || isProcessing,
706
714
  className: "absolute bottom-3 right-3 p-2 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white rounded-md transition-colors shadow-sm"
707
715
  },
708
716
  isProcessing ? /* @__PURE__ */ React69__namespace.default.createElement(lucideReact.Loader2, { className: "animate-spin", size: 18 }) : /* @__PURE__ */ React69__namespace.default.createElement(lucideReact.Send, { size: 18 })
@@ -890,13 +898,13 @@ var requestJson = async (options) => {
890
898
  body: body ? JSON.stringify(body) : void 0,
891
899
  signal: controller?.signal
892
900
  });
893
- const text5 = await response.text();
901
+ const text6 = await response.text();
894
902
  let data = null;
895
- if (text5) {
903
+ if (text6) {
896
904
  try {
897
- data = JSON.parse(text5);
905
+ data = JSON.parse(text6);
898
906
  } catch {
899
- data = text5;
907
+ data = text6;
900
908
  }
901
909
  }
902
910
  if (!response.ok) {
@@ -1357,20 +1365,20 @@ var japaneseUtils = {
1357
1365
  /**
1358
1366
  * 提取文本中的汉字
1359
1367
  */
1360
- extractKanji(text5) {
1361
- return text5.match(/[\u4E00-\u9FAF]/g) || [];
1368
+ extractKanji(text6) {
1369
+ return text6.match(/[\u4E00-\u9FAF]/g) || [];
1362
1370
  },
1363
1371
  /**
1364
1372
  * 提取文本中的假名
1365
1373
  */
1366
- extractKana(text5) {
1367
- return text5.match(/[\u3040-\u309F\u30A0-\u30FF]/g) || [];
1374
+ extractKana(text6) {
1375
+ return text6.match(/[\u3040-\u309F\u30A0-\u30FF]/g) || [];
1368
1376
  },
1369
1377
  /**
1370
1378
  * 清理文本,移除特殊字符但保留日语字符
1371
1379
  */
1372
- cleanText(text5) {
1373
- return text5.replace(/[^\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FAF\w\s]/g, "");
1380
+ cleanText(text6) {
1381
+ return text6.replace(/[^\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FAF\w\s]/g, "");
1374
1382
  }
1375
1383
  };
1376
1384
 
@@ -1459,11 +1467,11 @@ var fileUtils = {
1459
1467
  * 生成唯一文件名
1460
1468
  */
1461
1469
  generateUniqueFileName(originalName) {
1462
- const timestamp5 = Date.now();
1470
+ const timestamp6 = Date.now();
1463
1471
  const random = Math.random().toString(36).substring(2, 15);
1464
1472
  const extension = this.getFileExtension(originalName);
1465
1473
  const baseName = originalName.replace("." + extension, "");
1466
- return extension ? baseName + "_" + timestamp5 + "_" + random + "." + extension : baseName + "_" + timestamp5 + "_" + random;
1474
+ return extension ? baseName + "_" + timestamp6 + "_" + random + "." + extension : baseName + "_" + timestamp6 + "_" + random;
1467
1475
  },
1468
1476
  /**
1469
1477
  * 验证文件名是否有效
@@ -1533,28 +1541,28 @@ var stringUtils = {
1533
1541
  /**
1534
1542
  * 截断文本
1535
1543
  */
1536
- truncate(text5, length, suffix = "...") {
1537
- if (text5.length <= length) return text5;
1538
- return text5.substring(0, length - suffix.length) + suffix;
1544
+ truncate(text6, length, suffix = "...") {
1545
+ if (text6.length <= length) return text6;
1546
+ return text6.substring(0, length - suffix.length) + suffix;
1539
1547
  },
1540
1548
  /**
1541
1549
  * 首字母大写
1542
1550
  */
1543
- capitalize(text5) {
1544
- if (!text5) return "";
1545
- return text5.charAt(0).toUpperCase() + text5.slice(1).toLowerCase();
1551
+ capitalize(text6) {
1552
+ if (!text6) return "";
1553
+ return text6.charAt(0).toUpperCase() + text6.slice(1).toLowerCase();
1546
1554
  },
1547
1555
  /**
1548
1556
  * 驼峰转下划线
1549
1557
  */
1550
- camelToSnake(text5) {
1551
- return text5.replace(/[A-Z]/g, (letter) => "_" + letter.toLowerCase());
1558
+ camelToSnake(text6) {
1559
+ return text6.replace(/[A-Z]/g, (letter) => "_" + letter.toLowerCase());
1552
1560
  },
1553
1561
  /**
1554
1562
  * 下划线转驼峰
1555
1563
  */
1556
- snakeToCamel(text5) {
1557
- return text5.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
1564
+ snakeToCamel(text6) {
1565
+ return text6.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
1558
1566
  },
1559
1567
  /**
1560
1568
  * 生成随机字符串
@@ -3655,7 +3663,7 @@ function ImageMappingPanel({
3655
3663
  uploadLabel = "\u4E0A\u4F20\u56FE\u7247",
3656
3664
  clearLabel = "\u6E05\u9664"
3657
3665
  }) {
3658
- const handleFileChange = (id, file) => {
3666
+ const handleFileChange = (id2, file) => {
3659
3667
  if (!file) {
3660
3668
  return;
3661
3669
  }
@@ -3667,14 +3675,14 @@ function ImageMappingPanel({
3667
3675
  }
3668
3676
  onChange({
3669
3677
  ...value,
3670
- [id]: base64
3678
+ [id2]: base64
3671
3679
  });
3672
3680
  };
3673
3681
  reader.readAsDataURL(file);
3674
3682
  };
3675
- const handleRemove = (id) => {
3683
+ const handleRemove = (id2) => {
3676
3684
  const next = { ...value };
3677
- delete next[id];
3685
+ delete next[id2];
3678
3686
  onChange(next);
3679
3687
  };
3680
3688
  return /* @__PURE__ */ React69__namespace.default.createElement("details", { className: cn("rounded-lg border border-slate-200 bg-white/80 p-3", className) }, /* @__PURE__ */ React69__namespace.default.createElement("summary", { className: "cursor-pointer text-sm font-semibold text-slate-700" }, title), /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "mt-3 grid grid-cols-1 gap-2 sm:grid-cols-2" }, items.map((item) => {
@@ -4814,8 +4822,8 @@ var FloatingMenuExample = () => {
4814
4822
  { id: 4, label: "\u5E2E\u52A9", icon: "\u2753" },
4815
4823
  { id: 5, label: "\u9000\u51FA", icon: "\u{1F6AA}" }
4816
4824
  ];
4817
- const handleMenuItemClick = (id) => {
4818
- console.log("\u70B9\u51FB\u4E86\u83DC\u5355\u9879: " + id);
4825
+ const handleMenuItemClick = (id2) => {
4826
+ console.log("\u70B9\u51FB\u4E86\u83DC\u5355\u9879: " + id2);
4819
4827
  };
4820
4828
  return /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "w-full h-screen bg-gray-100 relative p-8" }, /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "max-w-2xl mx-auto bg-white rounded-2xl shadow-sm p-8 mt-12" }, /* @__PURE__ */ React69__namespace.default.createElement("h1", { className: "text-3xl font-bold mb-4 text-gray-900" }, "\u60AC\u6D6E\u83DC\u5355\u793A\u4F8B"), /* @__PURE__ */ React69__namespace.default.createElement("p", { className: "text-gray-600 leading-relaxed mb-6" }, "\u8FD9\u662F\u4E00\u4E2A\u53EF\u62D6\u62FD\u7684\u60AC\u6D6E\u83DC\u5355\u7EC4\u4EF6\u793A\u4F8B\u3002\u4F60\u53EF\u4EE5\u5C1D\u8BD5\u62D6\u52A8\u4E0B\u65B9\u7684 ", /* @__PURE__ */ React69__namespace.default.createElement("span", { className: "font-bold text-blue-600" }, "\u84DD\u8272\u6309\u94AE"), " \u5230\u5904\u79FB\u52A8\u3002"), /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "bg-blue-50 border-l-4 border-blue-500 p-4 mb-6" }, /* @__PURE__ */ React69__namespace.default.createElement("p", { className: "text-sm text-blue-700" }, /* @__PURE__ */ React69__namespace.default.createElement("strong", null, "\u667A\u80FD\u5B9A\u4F4D\uFF1A"), " \u83DC\u5355\u4F1A\u6839\u636E\u6309\u94AE\u5728\u5C4F\u5E55\u4E0A\u7684\u4F4D\u7F6E\u81EA\u52A8\u8C03\u6574\u5F39\u51FA\u65B9\u5411\uFF08\u5411\u5DE6\u6216\u5411\u53F3\uFF09\u3002"))), /* @__PURE__ */ React69__namespace.default.createElement(
4821
4829
  FloatingMenu_default,
@@ -5673,9 +5681,9 @@ var UserInfoBar = ({ apiClient }) => {
5673
5681
  ))));
5674
5682
  };
5675
5683
  function DanmakuPanel({ onSend }) {
5676
- const [text5, setText] = React69.useState("");
5684
+ const [text6, setText] = React69.useState("");
5677
5685
  const emit = () => {
5678
- const value = text5.trim();
5686
+ const value = text6.trim();
5679
5687
  if (!value) {
5680
5688
  return;
5681
5689
  }
@@ -5686,7 +5694,7 @@ function DanmakuPanel({ onSend }) {
5686
5694
  "input",
5687
5695
  {
5688
5696
  type: "text",
5689
- value: text5,
5697
+ value: text6,
5690
5698
  onChange: (event) => setText(event.target.value),
5691
5699
  onKeyDown: (event) => {
5692
5700
  if (event.key === "Enter") {
@@ -5786,8 +5794,8 @@ function FireworksControlPanel({
5786
5794
  function useDanmakuController(options) {
5787
5795
  const [items, setItems] = React69.useState([]);
5788
5796
  const cursorRef = React69.useRef(0);
5789
- const removeItem = React69.useCallback((id) => {
5790
- setItems((prev) => prev.filter((item) => item.id !== id));
5797
+ const removeItem = React69.useCallback((id2) => {
5798
+ setItems((prev) => prev.filter((item) => item.id !== id2));
5791
5799
  }, []);
5792
5800
  const addIncoming = React69.useCallback((message) => {
5793
5801
  setItems((prev) => {
@@ -5802,8 +5810,8 @@ function useDanmakuController(options) {
5802
5810
  cursorRef.current += 1;
5803
5811
  }, []);
5804
5812
  const send = React69.useCallback(
5805
- (text5, color, sendOptions) => {
5806
- const trimmed = text5.trim();
5813
+ (text6, color, sendOptions) => {
5814
+ const trimmed = text6.trim();
5807
5815
  if (!trimmed) {
5808
5816
  return null;
5809
5817
  }
@@ -5840,26 +5848,26 @@ function useDanmakuController(options) {
5840
5848
  [addIncoming, items, removeItem, send]
5841
5849
  );
5842
5850
  }
5843
- function parseCommand(text5) {
5844
- if (text5.startsWith("/miku ")) {
5845
- return { launchKind: "miku", content: text5.replace("/miku ", "").trim() };
5851
+ function parseCommand(text6) {
5852
+ if (text6.startsWith("/miku ")) {
5853
+ return { launchKind: "miku", content: text6.replace("/miku ", "").trim() };
5846
5854
  }
5847
- if (text5 === "/miku") {
5855
+ if (text6 === "/miku") {
5848
5856
  return { launchKind: "miku", content: "MIKU!" };
5849
5857
  }
5850
- if (text5.startsWith("/avatar ")) {
5851
- return { launchKind: "avatar", content: text5.replace("/avatar ", "").trim() };
5858
+ if (text6.startsWith("/avatar ")) {
5859
+ return { launchKind: "avatar", content: text6.replace("/avatar ", "").trim() };
5852
5860
  }
5853
- if (text5 === "/avatar") {
5861
+ if (text6 === "/avatar") {
5854
5862
  return { launchKind: "avatar", content: "Avatar Firework!" };
5855
5863
  }
5856
- if (text5.startsWith("/normal ")) {
5857
- return { launchKind: "normal", content: text5.replace("/normal ", "").trim() };
5864
+ if (text6.startsWith("/normal ")) {
5865
+ return { launchKind: "normal", content: text6.replace("/normal ", "").trim() };
5858
5866
  }
5859
- if (text5 === "/normal") {
5867
+ if (text6 === "/normal") {
5860
5868
  return { launchKind: "normal", content: "Fireworks!" };
5861
5869
  }
5862
- return { content: text5 };
5870
+ return { content: text6 };
5863
5871
  }
5864
5872
  function createCircularSpriteTexture() {
5865
5873
  const size = 64;
@@ -6541,12 +6549,12 @@ var WebSocketTransport = class {
6541
6549
  }
6542
6550
  };
6543
6551
  function parseServerMessage(raw) {
6544
- const text5 = decodeMessage(raw);
6545
- if (!text5) {
6552
+ const text6 = decodeMessage(raw);
6553
+ if (!text6) {
6546
6554
  return null;
6547
6555
  }
6548
6556
  try {
6549
- return JSON.parse(text5);
6557
+ return JSON.parse(text6);
6550
6558
  } catch {
6551
6559
  return null;
6552
6560
  }
@@ -6813,8 +6821,8 @@ function MikuFireworks3D({
6813
6821
  }
6814
6822
  launch(payload);
6815
6823
  };
6816
- const handleSendDanmaku = (text5) => {
6817
- const result = send(text5, void 0, {
6824
+ const handleSendDanmaku = (text6) => {
6825
+ const result = send(text6, void 0, {
6818
6826
  optimistic: !realtimeEnabled
6819
6827
  });
6820
6828
  if (!result) {
@@ -6913,9 +6921,9 @@ function useScreenReceiver(options) {
6913
6921
  const peerRef = React69.useRef({ pendingCandidates: [] });
6914
6922
  const videoRef = React69.useRef(null);
6915
6923
  const appendLog = React69.useCallback(
6916
- (text5) => {
6924
+ (text6) => {
6917
6925
  logIdRef.current += 1;
6918
- setLogs((prev) => [...prev, { id: logIdRef.current, text: text5 }].slice(-maxLogs));
6926
+ setLogs((prev) => [...prev, { id: logIdRef.current, text: text6 }].slice(-maxLogs));
6919
6927
  },
6920
6928
  [maxLogs]
6921
6929
  );
@@ -7641,8 +7649,8 @@ var withRoundedClip = (ctx, left, top, width, height, radius, draw) => {
7641
7649
  draw();
7642
7650
  ctx.restore();
7643
7651
  };
7644
- var drawMultilineText = (ctx, text5, left, top, maxWidth, lineHeight) => {
7645
- const paragraphs = text5.split("\n");
7652
+ var drawMultilineText = (ctx, text6, left, top, maxWidth, lineHeight) => {
7653
+ const paragraphs = text6.split("\n");
7646
7654
  let currentY = top;
7647
7655
  paragraphs.forEach((paragraph, index) => {
7648
7656
  const words = paragraph.split("");
@@ -8307,19 +8315,19 @@ var FestivalCardConfigPage = ({
8307
8315
  const createNew = async () => {
8308
8316
  const name = window.prompt("\u8BF7\u8F93\u5165\u65B0\u5361\u7247\u540D\u79F0");
8309
8317
  if (!name) return;
8310
- const id = `festival-${Date.now()}`;
8318
+ const id2 = `festival-${Date.now()}`;
8311
8319
  const config = normalizeFestivalCardConfig({
8312
- id,
8320
+ id: id2,
8313
8321
  name
8314
8322
  });
8315
8323
  try {
8316
- const response = await fetch(`${apiBase}/${encodeURIComponent(id)}`, {
8324
+ const response = await fetch(`${apiBase}/${encodeURIComponent(id2)}`, {
8317
8325
  method: "PUT",
8318
8326
  headers: { "Content-Type": "application/json" },
8319
8327
  body: JSON.stringify({ config })
8320
8328
  });
8321
8329
  if (!response.ok) throw new Error(`\u521B\u5EFA\u5931\u8D25: ${response.status}`);
8322
- setSelectedId(id);
8330
+ setSelectedId(id2);
8323
8331
  await reloadList();
8324
8332
  window.alert("\u65B0\u5361\u7247\u521B\u5EFA\u6210\u529F");
8325
8333
  } catch (error) {
@@ -8424,21 +8432,21 @@ if (!memoryStore.has(defaultId)) {
8424
8432
  var createInMemoryFestivalCardDb = () => ({
8425
8433
  listConfigs() {
8426
8434
  return Promise.resolve(
8427
- Array.from(memoryStore.entries()).map(([id, config]) => ({
8428
- id,
8429
- name: config.name || id
8435
+ Array.from(memoryStore.entries()).map(([id2, config]) => ({
8436
+ id: id2,
8437
+ name: config.name || id2
8430
8438
  }))
8431
8439
  );
8432
8440
  },
8433
- getConfig(id) {
8434
- return Promise.resolve(memoryStore.get(id) || null);
8441
+ getConfig(id2) {
8442
+ return Promise.resolve(memoryStore.get(id2) || null);
8435
8443
  },
8436
- saveConfig(id, config) {
8437
- memoryStore.set(id, config);
8444
+ saveConfig(id2, config) {
8445
+ memoryStore.set(id2, config);
8438
8446
  return Promise.resolve();
8439
8447
  },
8440
- deleteConfig(id) {
8441
- memoryStore.delete(id);
8448
+ deleteConfig(id2) {
8449
+ memoryStore.delete(id2);
8442
8450
  return Promise.resolve();
8443
8451
  }
8444
8452
  });
@@ -8799,6 +8807,1592 @@ var BoothConfigPage = ({ initialConfig, onSave }) => {
8799
8807
  )), /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React69__namespace.default.createElement("button", { className: "rounded bg-indigo-600 px-3 py-2 text-white", disabled: saving, onClick: save }, saving ? "\u4FDD\u5B58\u4E2D..." : "\u4FDD\u5B58\u914D\u7F6E"), /* @__PURE__ */ React69__namespace.default.createElement("button", { className: "rounded border px-3 py-2", onClick: reset }, "\u6062\u590D\u9ED8\u8BA4")));
8800
8808
  };
8801
8809
 
8810
+ // src/mikuContest/logic/shared/defaults.ts
8811
+ var defaultMikuVotingRules = {
8812
+ maxVotesPerDay: 3,
8813
+ forbidDuplicateVotePerWork: true,
8814
+ maxVotesPerDevicePerDay: 20,
8815
+ maxVotesPerIpPerDay: 100
8816
+ };
8817
+ var createDefaultMikuContestConfig = (overrides) => ({
8818
+ id: overrides?.id || "miku-contest-default",
8819
+ name: overrides?.name || "\u521D\u97F3\u672A\u6765\u793E\u56E2\u5F81\u7A3F\u5927\u8D5B",
8820
+ theme: overrides?.theme || "\u521D\u97F3\u672A\u6765\u4E3B\u9898\u521B\u4F5C\u5F81\u7A3F",
8821
+ organizer: overrides?.organizer || "\u521D\u97F3\u672A\u6765\u793E\u56E2",
8822
+ awards: overrides?.awards || ["\u4E00\u7B49\u5956", "\u4E8C\u7B49\u5956", "\u4E09\u7B49\u5956", "\u4EBA\u6C14\u5956"],
8823
+ rules: overrides?.rules || "\u8BF7\u786E\u4FDD\u4F5C\u54C1\u539F\u521B\u4E14\u7B26\u5408\u793E\u56E2\u89C4\u8303\u3002",
8824
+ copyright: overrides?.copyright || "\u6295\u7A3F\u5373\u89C6\u4E3A\u6388\u6743\u8D5B\u4E8B\u5C55\u793A\u4E0E\u516C\u793A\u3002",
8825
+ timeline: overrides?.timeline || {
8826
+ submissionStartAt: (/* @__PURE__ */ new Date()).toISOString(),
8827
+ submissionEndAt: (/* @__PURE__ */ new Date()).toISOString(),
8828
+ votingStartAt: (/* @__PURE__ */ new Date()).toISOString(),
8829
+ votingEndAt: (/* @__PURE__ */ new Date()).toISOString(),
8830
+ publicResultAt: (/* @__PURE__ */ new Date()).toISOString()
8831
+ },
8832
+ votingRules: {
8833
+ ...defaultMikuVotingRules,
8834
+ ...overrides?.votingRules || {}
8835
+ },
8836
+ toggles: {
8837
+ submissionEnabled: overrides?.toggles?.submissionEnabled ?? true,
8838
+ votingEnabled: overrides?.toggles?.votingEnabled ?? true,
8839
+ resultEnabled: overrides?.toggles?.resultEnabled ?? false
8840
+ }
8841
+ });
8842
+
8843
+ // src/mikuContest/logic/shared/validators.ts
8844
+ var DESCRIPTION_LIMIT = 500;
8845
+ var MINIAPP_DESCRIPTION_LIMIT = 200;
8846
+ var TEXT_CONTENT_LIMIT = 2e3;
8847
+ var MAX_TAGS = 3;
8848
+ var hasValue = (value) => {
8849
+ return typeof value === "string" ? value.trim().length > 0 : value !== null && value !== void 0;
8850
+ };
8851
+ var validateByType = (type, input) => {
8852
+ const errors = [];
8853
+ const { content } = input;
8854
+ switch (type) {
8855
+ case "visual": {
8856
+ const imageCount = content.images?.length || 0;
8857
+ if (imageCount < 1 || imageCount > 3) {
8858
+ errors.push("\u89C6\u89C9\u7C7B\u4F5C\u54C1\u9700\u4E0A\u4F20 1-3 \u5F20\u56FE\u7247");
8859
+ }
8860
+ break;
8861
+ }
8862
+ case "video": {
8863
+ if (!hasValue(content.videoLink)) {
8864
+ errors.push("\u89C6\u9891\u7C7B\u4F5C\u54C1\u9700\u63D0\u4F9B\u89C6\u9891\u94FE\u63A5");
8865
+ }
8866
+ if (!hasValue(content.coverImage)) {
8867
+ errors.push("\u89C6\u9891\u7C7B\u4F5C\u54C1\u9700\u63D0\u4F9B\u5C01\u9762\u56FE");
8868
+ }
8869
+ break;
8870
+ }
8871
+ case "text": {
8872
+ const text6 = content.textContent || "";
8873
+ if (!text6.trim()) {
8874
+ errors.push("\u6587\u5B57\u7C7B\u4F5C\u54C1\u9700\u586B\u5199\u6B63\u6587");
8875
+ }
8876
+ if (text6.length > TEXT_CONTENT_LIMIT) {
8877
+ errors.push(`\u6587\u5B57\u6B63\u6587\u4E0D\u80FD\u8D85\u8FC7 ${TEXT_CONTENT_LIMIT} \u5B57`);
8878
+ }
8879
+ break;
8880
+ }
8881
+ case "audio": {
8882
+ if (!hasValue(content.audioLink)) {
8883
+ errors.push("\u97F3\u9891\u7C7B\u4F5C\u54C1\u9700\u63D0\u4F9B\u97F3\u9891\u94FE\u63A5");
8884
+ }
8885
+ break;
8886
+ }
8887
+ }
8888
+ return errors;
8889
+ };
8890
+ var validateMikuSubmissionInput = (input, mode = "web") => {
8891
+ const errors = [];
8892
+ if (!input.contestId.trim()) errors.push("contestId \u4E0D\u80FD\u4E3A\u7A7A");
8893
+ if (!input.authorId.trim()) errors.push("authorId \u4E0D\u80FD\u4E3A\u7A7A");
8894
+ if (!input.authorNickname.trim()) errors.push("\u4F5C\u8005\u6635\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
8895
+ if (!input.title.trim()) errors.push("\u4F5C\u54C1\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
8896
+ const descriptionLimit = mode === "miniapp" ? MINIAPP_DESCRIPTION_LIMIT : DESCRIPTION_LIMIT;
8897
+ if (input.description.length > descriptionLimit) {
8898
+ errors.push(`\u4F5C\u54C1\u7B80\u4ECB\u4E0D\u80FD\u8D85\u8FC7 ${descriptionLimit} \u5B57`);
8899
+ }
8900
+ if ((input.tags?.length || 0) > MAX_TAGS) {
8901
+ errors.push(`\u6807\u7B7E\u6700\u591A ${MAX_TAGS} \u4E2A`);
8902
+ }
8903
+ errors.push(...validateByType(input.type, input));
8904
+ return errors;
8905
+ };
8906
+
8907
+ // src/mikuContest/logic/shared/voting.ts
8908
+ var toVoteDayKey = (date = /* @__PURE__ */ new Date()) => {
8909
+ const y = date.getUTCFullYear();
8910
+ const m = String(date.getUTCMonth() + 1).padStart(2, "0");
8911
+ const d = String(date.getUTCDate()).padStart(2, "0");
8912
+ return `${y}-${m}-${d}`;
8913
+ };
8914
+ var checkVoteEligibility = (context) => {
8915
+ const { existingVotes, submissionId, voterId, dayKey, rules } = context;
8916
+ const userTodayVotes = existingVotes.filter((vote) => vote.voterId === voterId && vote.dayKey === dayKey);
8917
+ if (userTodayVotes.length >= rules.maxVotesPerDay) {
8918
+ return { ok: false, reason: "\u5DF2\u8FBE\u5230\u4ECA\u65E5\u6295\u7968\u4E0A\u9650" };
8919
+ }
8920
+ if (rules.forbidDuplicateVotePerWork) {
8921
+ const duplicated = userTodayVotes.some((vote) => vote.submissionId === submissionId);
8922
+ if (duplicated) return { ok: false, reason: "\u4E0D\u53EF\u91CD\u590D\u6295\u540C\u4E00\u4F5C\u54C1" };
8923
+ }
8924
+ return { ok: true };
8925
+ };
8926
+ var sortByVotesDesc = (items) => {
8927
+ return [...items].sort((a, b) => {
8928
+ if (b.voteCount === a.voteCount) {
8929
+ return (a.createdAt || "").localeCompare(b.createdAt || "");
8930
+ }
8931
+ return b.voteCount - a.voteCount;
8932
+ });
8933
+ };
8934
+ var randomId = (prefix) => {
8935
+ return `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
8936
+ };
8937
+ var serialNo = () => {
8938
+ const now = /* @__PURE__ */ new Date();
8939
+ const y = now.getFullYear();
8940
+ const m = String(now.getMonth() + 1).padStart(2, "0");
8941
+ const d = String(now.getDate()).padStart(2, "0");
8942
+ const seq = Math.floor(Math.random() * 9e3 + 1e3);
8943
+ return `MIKU-${y}${m}${d}-${seq}`;
8944
+ };
8945
+ var MikuContestService = class {
8946
+ constructor(options = {}) {
8947
+ this.submissions = /* @__PURE__ */ new Map();
8948
+ this.votes = [];
8949
+ this.announcements = /* @__PURE__ */ new Map();
8950
+ this.voterRestrictions = /* @__PURE__ */ new Map();
8951
+ this.contest = createDefaultMikuContestConfig(options.contestConfig);
8952
+ }
8953
+ getContestConfig() {
8954
+ return this.contest;
8955
+ }
8956
+ updateContestConfig(patch) {
8957
+ this.contest = {
8958
+ ...this.contest,
8959
+ ...patch,
8960
+ votingRules: {
8961
+ ...this.contest.votingRules,
8962
+ ...patch.votingRules || {}
8963
+ },
8964
+ toggles: {
8965
+ ...this.contest.toggles,
8966
+ ...patch.toggles || {}
8967
+ },
8968
+ timeline: {
8969
+ ...this.contest.timeline,
8970
+ ...patch.timeline || {}
8971
+ }
8972
+ };
8973
+ return this.contest;
8974
+ }
8975
+ createSubmission(input, mode = "web") {
8976
+ if (!this.contest.toggles.submissionEnabled) {
8977
+ throw new Error("\u5F53\u524D\u672A\u5F00\u653E\u6295\u7A3F");
8978
+ }
8979
+ const errors = validateMikuSubmissionInput(input, mode);
8980
+ if (errors.length > 0) {
8981
+ throw new Error(errors.join("\uFF1B"));
8982
+ }
8983
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8984
+ const next = {
8985
+ id: randomId("submission"),
8986
+ serialNo: serialNo(),
8987
+ contestId: input.contestId,
8988
+ authorId: input.authorId,
8989
+ authorNickname: input.authorNickname,
8990
+ title: input.title,
8991
+ type: input.type,
8992
+ description: input.description,
8993
+ tags: input.tags || [],
8994
+ content: input.content,
8995
+ voteCount: 0,
8996
+ status: "pending",
8997
+ createdAt: now,
8998
+ updatedAt: now
8999
+ };
9000
+ this.submissions.set(next.id, next);
9001
+ return next;
9002
+ }
9003
+ listSubmissions(filter) {
9004
+ const authorKeyword = filter?.authorKeyword?.trim().toLowerCase();
9005
+ const titleKeyword = filter?.titleKeyword?.trim().toLowerCase();
9006
+ return [...this.submissions.values()].filter((item) => {
9007
+ if (filter?.status && item.status !== filter.status) return false;
9008
+ if (filter?.type && item.type !== filter.type) return false;
9009
+ if (filter?.authorId && item.authorId !== filter.authorId) return false;
9010
+ if (authorKeyword && !item.authorNickname.toLowerCase().includes(authorKeyword)) return false;
9011
+ if (titleKeyword && !item.title.toLowerCase().includes(titleKeyword)) return false;
9012
+ return true;
9013
+ });
9014
+ }
9015
+ getSubmission(submissionId) {
9016
+ return this.submissions.get(submissionId) || null;
9017
+ }
9018
+ reviewSubmission(input) {
9019
+ const current = this.submissions.get(input.submissionId);
9020
+ if (!current) throw new Error("\u6295\u7A3F\u4E0D\u5B58\u5728");
9021
+ if (input.action === "reject" && !input.rejectReason?.trim()) {
9022
+ throw new Error("\u9A73\u56DE\u9700\u586B\u5199\u539F\u56E0");
9023
+ }
9024
+ const reviewed = {
9025
+ ...current,
9026
+ status: input.action === "approve" ? "approved" : "rejected",
9027
+ rejectReason: input.action === "reject" ? input.rejectReason?.trim() : void 0,
9028
+ reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
9029
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
9030
+ };
9031
+ this.submissions.set(reviewed.id, reviewed);
9032
+ return reviewed;
9033
+ }
9034
+ vote(input) {
9035
+ if (!this.contest.toggles.votingEnabled) {
9036
+ throw new Error("\u5F53\u524D\u672A\u5F00\u653E\u6295\u7968");
9037
+ }
9038
+ const restriction = this.voterRestrictions.get(input.voterId);
9039
+ if (restriction?.banned) {
9040
+ throw new Error("\u5F53\u524D\u8D26\u53F7\u5DF2\u88AB\u9650\u5236\u6295\u7968");
9041
+ }
9042
+ const target = this.submissions.get(input.submissionId);
9043
+ if (!target) throw new Error("\u4F5C\u54C1\u4E0D\u5B58\u5728");
9044
+ if (target.status !== "approved") throw new Error("\u4EC5\u53EF\u5BF9\u5DF2\u8FC7\u5BA1\u4F5C\u54C1\u6295\u7968");
9045
+ const dayKey = toVoteDayKey();
9046
+ const eligible = checkVoteEligibility({
9047
+ existingVotes: this.votes,
9048
+ submissionId: input.submissionId,
9049
+ voterId: input.voterId,
9050
+ dayKey,
9051
+ rules: this.contest.votingRules
9052
+ });
9053
+ if (!eligible.ok) {
9054
+ throw new Error(eligible.reason || "\u6295\u7968\u5931\u8D25");
9055
+ }
9056
+ const vote = {
9057
+ id: randomId("vote"),
9058
+ contestId: input.contestId,
9059
+ submissionId: input.submissionId,
9060
+ voterId: input.voterId,
9061
+ votedAt: (/* @__PURE__ */ new Date()).toISOString(),
9062
+ dayKey,
9063
+ deviceId: input.deviceId,
9064
+ ip: input.ip
9065
+ };
9066
+ this.votes.push(vote);
9067
+ const updated = {
9068
+ ...target,
9069
+ voteCount: target.voteCount + 1,
9070
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
9071
+ };
9072
+ this.submissions.set(updated.id, updated);
9073
+ return updated;
9074
+ }
9075
+ getVoterRestriction(voterId) {
9076
+ return this.voterRestrictions.get(voterId) || null;
9077
+ }
9078
+ setVoterRestriction(input) {
9079
+ const next = {
9080
+ voterId: input.voterId,
9081
+ banned: input.banned,
9082
+ reason: input.reason,
9083
+ operatorId: input.operatorId,
9084
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
9085
+ };
9086
+ this.voterRestrictions.set(input.voterId, next);
9087
+ return next;
9088
+ }
9089
+ resetVotes(input) {
9090
+ if (!input.submissionId && !input.voterId) {
9091
+ throw new Error("submissionId \u4E0E voterId \u81F3\u5C11\u63D0\u4F9B\u4E00\u4E2A");
9092
+ }
9093
+ const before = this.votes.length;
9094
+ const affected = /* @__PURE__ */ new Set();
9095
+ const remained = this.votes.filter((vote) => {
9096
+ const matchSubmission = input.submissionId ? vote.submissionId === input.submissionId : true;
9097
+ const matchVoter = input.voterId ? vote.voterId === input.voterId : true;
9098
+ const shouldRemove = matchSubmission && matchVoter;
9099
+ if (shouldRemove) affected.add(vote.submissionId);
9100
+ return !shouldRemove;
9101
+ });
9102
+ this.votes.length = 0;
9103
+ this.votes.push(...remained);
9104
+ this.recalculateVoteCounts();
9105
+ return {
9106
+ removedVotes: before - remained.length,
9107
+ affectedSubmissions: [...affected]
9108
+ };
9109
+ }
9110
+ listAnnouncements(contestId) {
9111
+ const all = [...this.announcements.values()];
9112
+ return contestId ? all.filter((item) => item.contestId === contestId) : all;
9113
+ }
9114
+ publishAnnouncement(input) {
9115
+ const now = (/* @__PURE__ */ new Date()).toISOString();
9116
+ const announcement = {
9117
+ id: randomId("notice"),
9118
+ contestId: input.contestId,
9119
+ title: input.title,
9120
+ content: input.content,
9121
+ type: input.type,
9122
+ createdBy: input.createdBy,
9123
+ createdAt: now,
9124
+ updatedAt: now
9125
+ };
9126
+ this.announcements.set(announcement.id, announcement);
9127
+ return announcement;
9128
+ }
9129
+ getLeaderboard(limit = 10) {
9130
+ const ranked = sortByVotesDesc(this.listSubmissions({ status: "approved" })).slice(0, limit);
9131
+ return ranked.map((item, index) => ({
9132
+ submissionId: item.id,
9133
+ title: item.title,
9134
+ authorNickname: item.authorNickname,
9135
+ voteCount: item.voteCount,
9136
+ rank: index + 1
9137
+ }));
9138
+ }
9139
+ getSnapshot() {
9140
+ return {
9141
+ contest: this.contest,
9142
+ submissions: this.listSubmissions(),
9143
+ announcements: this.listAnnouncements(),
9144
+ leaderboard: this.getLeaderboard()
9145
+ };
9146
+ }
9147
+ getSubmissionExportRows(filter) {
9148
+ return this.listSubmissions(filter).map((item) => ({
9149
+ \u6295\u7A3F\u7F16\u53F7: item.serialNo,
9150
+ \u6295\u7A3FID: item.id,
9151
+ \u8D5B\u4E8BID: item.contestId,
9152
+ \u4F5C\u8005ID: item.authorId,
9153
+ \u4F5C\u8005\u6635\u79F0: item.authorNickname,
9154
+ \u4F5C\u54C1\u540D\u79F0: item.title,
9155
+ \u4F5C\u54C1\u7C7B\u578B: item.type,
9156
+ \u7B80\u4ECB: item.description,
9157
+ \u6807\u7B7E: item.tags.join(","),
9158
+ \u5BA1\u6838\u72B6\u6001: item.status,
9159
+ \u9A73\u56DE\u539F\u56E0: item.rejectReason || "",
9160
+ \u7968\u6570: item.voteCount,
9161
+ \u63D0\u4EA4\u65F6\u95F4: item.createdAt,
9162
+ \u66F4\u65B0\u65F6\u95F4: item.updatedAt
9163
+ }));
9164
+ }
9165
+ exportSubmissionExcel(filter) {
9166
+ const rows = this.getSubmissionExportRows(filter);
9167
+ const workbook = XLSX__namespace.utils.book_new();
9168
+ const worksheet = XLSX__namespace.utils.json_to_sheet(rows);
9169
+ XLSX__namespace.utils.book_append_sheet(workbook, worksheet, "submissions");
9170
+ return XLSX__namespace.write(workbook, { bookType: "xlsx", type: "buffer" });
9171
+ }
9172
+ recalculateVoteCounts() {
9173
+ const counts = /* @__PURE__ */ new Map();
9174
+ for (const vote of this.votes) {
9175
+ counts.set(vote.submissionId, (counts.get(vote.submissionId) || 0) + 1);
9176
+ }
9177
+ for (const [id2, submission] of this.submissions.entries()) {
9178
+ const nextCount = counts.get(id2) || 0;
9179
+ if (submission.voteCount === nextCount) continue;
9180
+ this.submissions.set(id2, {
9181
+ ...submission,
9182
+ voteCount: nextCount,
9183
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
9184
+ });
9185
+ }
9186
+ }
9187
+ exportPersistenceState() {
9188
+ return {
9189
+ contest: this.contest,
9190
+ submissions: [...this.submissions.values()],
9191
+ votes: [...this.votes],
9192
+ announcements: [...this.announcements.values()],
9193
+ voterRestrictions: [...this.voterRestrictions.values()],
9194
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
9195
+ };
9196
+ }
9197
+ importPersistenceState(state) {
9198
+ this.contest = state.contest;
9199
+ this.submissions.clear();
9200
+ this.announcements.clear();
9201
+ this.voterRestrictions.clear();
9202
+ this.votes.length = 0;
9203
+ for (const item of state.submissions) {
9204
+ this.submissions.set(item.id, item);
9205
+ }
9206
+ for (const item of state.announcements) {
9207
+ this.announcements.set(item.id, item);
9208
+ }
9209
+ for (const item of state.voterRestrictions) {
9210
+ this.voterRestrictions.set(item.voterId, item);
9211
+ }
9212
+ this.votes.push(...state.votes);
9213
+ }
9214
+ };
9215
+ var createMikuContestService = (options) => {
9216
+ return new MikuContestService(options);
9217
+ };
9218
+ var mikuContestConfigs = pgCore.pgTable("miku_contest_configs", {
9219
+ contestId: pgCore.text("contest_id").primaryKey(),
9220
+ config: pgCore.jsonb("config").$type().notNull(),
9221
+ createdAt: pgCore.timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
9222
+ updatedAt: pgCore.timestamp("updated_at", { withTimezone: true }).defaultNow().notNull()
9223
+ });
9224
+ var mikuContestSubmissions = pgCore.pgTable("miku_contest_submissions", {
9225
+ id: pgCore.text("id").primaryKey(),
9226
+ contestId: pgCore.text("contest_id").notNull(),
9227
+ serialNo: pgCore.text("serial_no").notNull(),
9228
+ authorId: pgCore.text("author_id").notNull(),
9229
+ authorNickname: pgCore.text("author_nickname").notNull(),
9230
+ title: pgCore.text("title").notNull(),
9231
+ type: pgCore.text("type").notNull(),
9232
+ description: pgCore.text("description").notNull(),
9233
+ tags: pgCore.jsonb("tags").$type().notNull(),
9234
+ content: pgCore.jsonb("content").$type().notNull(),
9235
+ voteCount: pgCore.integer("vote_count").notNull().default(0),
9236
+ status: pgCore.text("status").notNull(),
9237
+ rejectReason: pgCore.text("reject_reason"),
9238
+ createdAt: pgCore.timestamp("created_at", { withTimezone: true }).notNull(),
9239
+ reviewedAt: pgCore.timestamp("reviewed_at", { withTimezone: true }),
9240
+ updatedAt: pgCore.timestamp("updated_at", { withTimezone: true }).notNull()
9241
+ });
9242
+ var mikuContestVotes = pgCore.pgTable("miku_contest_votes", {
9243
+ id: pgCore.text("id").primaryKey(),
9244
+ contestId: pgCore.text("contest_id").notNull(),
9245
+ submissionId: pgCore.text("submission_id").notNull(),
9246
+ voterId: pgCore.text("voter_id").notNull(),
9247
+ votedAt: pgCore.text("voted_at").notNull(),
9248
+ dayKey: pgCore.text("day_key").notNull(),
9249
+ deviceId: pgCore.text("device_id"),
9250
+ ip: pgCore.text("ip")
9251
+ });
9252
+ var mikuContestNotices = pgCore.pgTable("miku_contest_notices", {
9253
+ id: pgCore.text("id").primaryKey(),
9254
+ contestId: pgCore.text("contest_id").notNull(),
9255
+ title: pgCore.text("title").notNull(),
9256
+ content: pgCore.text("content").notNull(),
9257
+ type: pgCore.text("type").notNull(),
9258
+ createdBy: pgCore.text("created_by").notNull(),
9259
+ createdAt: pgCore.text("created_at").notNull(),
9260
+ updatedAt: pgCore.text("updated_at").notNull()
9261
+ });
9262
+ var mikuContestVoterRestrictions = pgCore.pgTable("miku_contest_voter_restrictions", {
9263
+ id: pgCore.text("id").primaryKey(),
9264
+ contestId: pgCore.text("contest_id").notNull(),
9265
+ data: pgCore.jsonb("data").$type().notNull()
9266
+ });
9267
+ var MikuContestStateDbService = class {
9268
+ constructor(db) {
9269
+ this.db = db;
9270
+ }
9271
+ async loadState(contestId) {
9272
+ const configRows = await this.db.select({ config: mikuContestConfigs.config }).from(mikuContestConfigs).where(drizzleOrm.eq(mikuContestConfigs.contestId, contestId)).limit(1);
9273
+ const config = configRows[0]?.config;
9274
+ if (!config) return null;
9275
+ const [submissions, votes, announcements, restrictions] = await Promise.all([
9276
+ this.db.select().from(mikuContestSubmissions).where(drizzleOrm.eq(mikuContestSubmissions.contestId, contestId)),
9277
+ this.db.select().from(mikuContestVotes).where(drizzleOrm.eq(mikuContestVotes.contestId, contestId)),
9278
+ this.db.select().from(mikuContestNotices).where(drizzleOrm.eq(mikuContestNotices.contestId, contestId)),
9279
+ this.db.select({ data: mikuContestVoterRestrictions.data }).from(mikuContestVoterRestrictions).where(drizzleOrm.eq(mikuContestVoterRestrictions.contestId, contestId))
9280
+ ]);
9281
+ return {
9282
+ contest: config,
9283
+ submissions,
9284
+ votes,
9285
+ announcements,
9286
+ voterRestrictions: restrictions.map((item) => item.data),
9287
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
9288
+ };
9289
+ }
9290
+ async saveState(state) {
9291
+ const contestId = state.contest.id;
9292
+ const exists = await this.db.select({ contestId: mikuContestConfigs.contestId }).from(mikuContestConfigs).where(drizzleOrm.eq(mikuContestConfigs.contestId, contestId)).limit(1);
9293
+ if (exists[0]) {
9294
+ await this.db.update(mikuContestConfigs).set({
9295
+ config: state.contest,
9296
+ updatedAt: /* @__PURE__ */ new Date()
9297
+ }).where(drizzleOrm.eq(mikuContestConfigs.contestId, contestId));
9298
+ } else {
9299
+ await this.db.insert(mikuContestConfigs).values({
9300
+ contestId,
9301
+ config: state.contest
9302
+ });
9303
+ }
9304
+ await this.db.delete(mikuContestSubmissions).where(drizzleOrm.eq(mikuContestSubmissions.contestId, contestId));
9305
+ await this.db.delete(mikuContestVotes).where(drizzleOrm.eq(mikuContestVotes.contestId, contestId));
9306
+ await this.db.delete(mikuContestNotices).where(drizzleOrm.eq(mikuContestNotices.contestId, contestId));
9307
+ await this.db.delete(mikuContestVoterRestrictions).where(drizzleOrm.eq(mikuContestVoterRestrictions.contestId, contestId));
9308
+ if (state.submissions.length > 0) {
9309
+ await this.db.insert(mikuContestSubmissions).values(
9310
+ state.submissions.map((item) => ({
9311
+ ...item,
9312
+ contestId,
9313
+ createdAt: new Date(item.createdAt),
9314
+ reviewedAt: item.reviewedAt ? new Date(item.reviewedAt) : null,
9315
+ updatedAt: new Date(item.updatedAt)
9316
+ }))
9317
+ );
9318
+ }
9319
+ if (state.votes.length > 0) {
9320
+ await this.db.insert(mikuContestVotes).values(state.votes);
9321
+ }
9322
+ if (state.announcements.length > 0) {
9323
+ await this.db.insert(mikuContestNotices).values(state.announcements);
9324
+ }
9325
+ if (state.voterRestrictions.length > 0) {
9326
+ await this.db.insert(mikuContestVoterRestrictions).values(
9327
+ state.voterRestrictions.map((item) => ({
9328
+ id: `${contestId}:${item.voterId}`,
9329
+ contestId,
9330
+ data: item
9331
+ }))
9332
+ );
9333
+ }
9334
+ }
9335
+ };
9336
+
9337
+ // src/mikuContest/server/persistence/drizzle-adapter.ts
9338
+ var createMikuContestDrizzlePersistenceAdapter = (db) => {
9339
+ const service = new MikuContestStateDbService(db);
9340
+ return {
9341
+ loadState: (contestId) => service.loadState(contestId),
9342
+ saveState: (state) => service.saveState(state)
9343
+ };
9344
+ };
9345
+
9346
+ // src/mikuContest/server/persistence/service.ts
9347
+ var MikuContestPersistentService = class {
9348
+ constructor(options) {
9349
+ this.options = options;
9350
+ this.hydrated = false;
9351
+ this.hydrationPromise = null;
9352
+ this.engine = createMikuContestService(options);
9353
+ }
9354
+ async ensureHydrated() {
9355
+ if (this.hydrated) return;
9356
+ if (this.hydrationPromise) return this.hydrationPromise;
9357
+ this.hydrationPromise = (async () => {
9358
+ const contestId = this.engine.getContestConfig().id;
9359
+ const loaded = await this.options.persistenceAdapter.loadState(contestId);
9360
+ if (loaded) {
9361
+ this.engine.importPersistenceState(loaded);
9362
+ } else {
9363
+ await this.options.persistenceAdapter.saveState(this.engine.exportPersistenceState());
9364
+ }
9365
+ this.hydrated = true;
9366
+ })();
9367
+ await this.hydrationPromise;
9368
+ }
9369
+ async persist() {
9370
+ await this.options.persistenceAdapter.saveState(this.engine.exportPersistenceState());
9371
+ }
9372
+ async getContestConfig() {
9373
+ await this.ensureHydrated();
9374
+ return this.engine.getContestConfig();
9375
+ }
9376
+ async updateContestConfig(patch) {
9377
+ await this.ensureHydrated();
9378
+ const data = this.engine.updateContestConfig(patch);
9379
+ await this.persist();
9380
+ return data;
9381
+ }
9382
+ async createSubmission(input, mode = "web") {
9383
+ await this.ensureHydrated();
9384
+ const data = this.engine.createSubmission(input, mode);
9385
+ await this.persist();
9386
+ return data;
9387
+ }
9388
+ async listSubmissions(filter) {
9389
+ await this.ensureHydrated();
9390
+ return this.engine.listSubmissions(filter);
9391
+ }
9392
+ async getSubmission(submissionId) {
9393
+ await this.ensureHydrated();
9394
+ return this.engine.getSubmission(submissionId);
9395
+ }
9396
+ async reviewSubmission(input) {
9397
+ await this.ensureHydrated();
9398
+ const data = this.engine.reviewSubmission(input);
9399
+ await this.persist();
9400
+ return data;
9401
+ }
9402
+ async vote(input) {
9403
+ await this.ensureHydrated();
9404
+ const data = this.engine.vote(input);
9405
+ await this.persist();
9406
+ return data;
9407
+ }
9408
+ async getVoterRestriction(voterId) {
9409
+ await this.ensureHydrated();
9410
+ return this.engine.getVoterRestriction(voterId);
9411
+ }
9412
+ async setVoterRestriction(input) {
9413
+ await this.ensureHydrated();
9414
+ const data = this.engine.setVoterRestriction(input);
9415
+ await this.persist();
9416
+ return data;
9417
+ }
9418
+ async resetVotes(input) {
9419
+ await this.ensureHydrated();
9420
+ const data = this.engine.resetVotes(input);
9421
+ await this.persist();
9422
+ return data;
9423
+ }
9424
+ async listAnnouncements(contestId) {
9425
+ await this.ensureHydrated();
9426
+ return this.engine.listAnnouncements(contestId);
9427
+ }
9428
+ async publishAnnouncement(input) {
9429
+ await this.ensureHydrated();
9430
+ const data = this.engine.publishAnnouncement(input);
9431
+ await this.persist();
9432
+ return data;
9433
+ }
9434
+ async getLeaderboard(limit = 10) {
9435
+ await this.ensureHydrated();
9436
+ return this.engine.getLeaderboard(limit);
9437
+ }
9438
+ async getSnapshot() {
9439
+ await this.ensureHydrated();
9440
+ return this.engine.getSnapshot();
9441
+ }
9442
+ async getSubmissionExportRows(filter) {
9443
+ await this.ensureHydrated();
9444
+ return this.engine.getSubmissionExportRows(filter);
9445
+ }
9446
+ async exportSubmissionExcel(filter) {
9447
+ await this.ensureHydrated();
9448
+ return this.engine.exportSubmissionExcel(filter);
9449
+ }
9450
+ };
9451
+ var createMikuContestPersistentService = (options) => {
9452
+ return new MikuContestPersistentService(options);
9453
+ };
9454
+
9455
+ // src/mikuContest/server/db.ts
9456
+ var MikuContestDbService = class {
9457
+ constructor() {
9458
+ this._db = null;
9459
+ }
9460
+ setDb(db) {
9461
+ this._db = db;
9462
+ }
9463
+ isConfigured() {
9464
+ return Boolean(this._db);
9465
+ }
9466
+ get db() {
9467
+ if (!this._db) {
9468
+ throw new Error("MikuContestDbService: Database instance not set. Call setDb(db) first.");
9469
+ }
9470
+ return this._db;
9471
+ }
9472
+ };
9473
+ var mikuContestDbService = new MikuContestDbService();
9474
+
9475
+ // src/mikuContest/logic/hooks/useMikuContest.ts
9476
+ var useMikuContest = (options) => {
9477
+ const [service] = React69.useState(() => createMikuContestService(options));
9478
+ const [version, setVersion] = React69.useState(0);
9479
+ const refresh = () => setVersion((value) => value + 1);
9480
+ const snapshot = React69.useMemo(() => {
9481
+ return service.getSnapshot();
9482
+ }, [service, version]);
9483
+ return {
9484
+ service,
9485
+ snapshot,
9486
+ refresh
9487
+ };
9488
+ };
9489
+
9490
+ // src/mikuContest/service/api/client.ts
9491
+ var toQueryString = (filter) => {
9492
+ if (!filter) return "";
9493
+ const params = new URLSearchParams();
9494
+ if (filter.status) params.set("status", filter.status);
9495
+ if (filter.type) params.set("type", filter.type);
9496
+ if (filter.authorId) params.set("authorId", filter.authorId);
9497
+ if (filter.authorKeyword) params.set("authorKeyword", filter.authorKeyword);
9498
+ if (filter.titleKeyword) params.set("titleKeyword", filter.titleKeyword);
9499
+ const query = params.toString();
9500
+ return query ? `?${query}` : "";
9501
+ };
9502
+ var unwrap = (result) => {
9503
+ if (!result.success || result.data === void 0) {
9504
+ throw new Error(result.error || "\u8BF7\u6C42\u5931\u8D25");
9505
+ }
9506
+ return result.data;
9507
+ };
9508
+ var createMikuContestApiClient = (basePath, requester2) => {
9509
+ return {
9510
+ async getSnapshot() {
9511
+ const result = await requester2(`${basePath}/contest`, { method: "GET" });
9512
+ return unwrap(result);
9513
+ },
9514
+ async updateContestConfig(patch) {
9515
+ const result = await requester2(`${basePath}/contest`, {
9516
+ method: "PATCH",
9517
+ body: patch
9518
+ });
9519
+ return unwrap(result);
9520
+ },
9521
+ async createSubmission(input, mode = "web") {
9522
+ const result = await requester2(`${basePath}/submissions`, {
9523
+ method: "POST",
9524
+ body: { payload: input, mode }
9525
+ });
9526
+ return unwrap(result);
9527
+ },
9528
+ async listSubmissions(filter) {
9529
+ const result = await requester2(
9530
+ `${basePath}/submissions${toQueryString(filter)}`,
9531
+ { method: "GET" }
9532
+ );
9533
+ return unwrap(result);
9534
+ },
9535
+ async reviewSubmission(input) {
9536
+ const result = await requester2(`${basePath}/submissions/review`, {
9537
+ method: "POST",
9538
+ body: input
9539
+ });
9540
+ return unwrap(result);
9541
+ },
9542
+ async vote(input) {
9543
+ const result = await requester2(`${basePath}/votes`, {
9544
+ method: "POST",
9545
+ body: input
9546
+ });
9547
+ return unwrap(result);
9548
+ },
9549
+ async setVoterRestriction(input) {
9550
+ const result = await requester2(`${basePath}/admin/voter-restrictions`, {
9551
+ method: "POST",
9552
+ body: input
9553
+ });
9554
+ return unwrap(result);
9555
+ },
9556
+ async resetVotes(input) {
9557
+ const result = await requester2(
9558
+ `${basePath}/admin/votes/reset`,
9559
+ {
9560
+ method: "POST",
9561
+ body: input
9562
+ }
9563
+ );
9564
+ return unwrap(result);
9565
+ },
9566
+ async exportSubmissions(filter) {
9567
+ const response = await fetch(`${basePath}/admin/submissions/export${toQueryString(filter)}`);
9568
+ if (!response.ok) {
9569
+ throw new Error(`\u5BFC\u51FA\u5931\u8D25: ${response.status}`);
9570
+ }
9571
+ return response.arrayBuffer();
9572
+ }
9573
+ };
9574
+ };
9575
+
9576
+ // src/mikuContest/service/web/index.ts
9577
+ var web_exports = {};
9578
+ __export(web_exports, {
9579
+ createMikuContestWebClient: () => createMikuContestWebClient
9580
+ });
9581
+
9582
+ // src/mikuContest/service/web/client.ts
9583
+ var defaultRequester = (options) => {
9584
+ const baseUrl = options.baseUrl || "";
9585
+ const commonHeaders = options.headers || {};
9586
+ return async (url, requestOptions) => {
9587
+ const response = await fetch(`${baseUrl}${url}`, {
9588
+ method: requestOptions?.method || "GET",
9589
+ headers: {
9590
+ "Content-Type": "application/json",
9591
+ ...commonHeaders
9592
+ },
9593
+ body: requestOptions?.body ? JSON.stringify(requestOptions.body) : void 0
9594
+ });
9595
+ const json = await response.json();
9596
+ return json;
9597
+ };
9598
+ };
9599
+ var createMikuContestWebClient = (options = {}) => {
9600
+ const basePath = options.basePath || "/api/miku-contest";
9601
+ return createMikuContestApiClient(basePath, defaultRequester(options));
9602
+ };
9603
+
9604
+ // src/mikuContest/service/miniapp/index.ts
9605
+ var miniapp_exports = {};
9606
+ __export(miniapp_exports, {
9607
+ createMikuContestMiniappClient: () => createMikuContestMiniappClient
9608
+ });
9609
+
9610
+ // src/mikuContest/service/miniapp/client.ts
9611
+ var createMikuContestMiniappClient = (options) => {
9612
+ const basePath = options.basePath || "/api/miku-contest";
9613
+ return createMikuContestApiClient(basePath, options.requester);
9614
+ };
9615
+ var isDrizzleDb = (value) => {
9616
+ if (!value || typeof value !== "object") return false;
9617
+ const candidate = value;
9618
+ return typeof candidate.select === "function" && typeof candidate.insert === "function" && typeof candidate.update === "function" && typeof candidate.delete === "function";
9619
+ };
9620
+ var resolveService = (config) => {
9621
+ if (config?.service) return config.service;
9622
+ const adapter = config?.persistenceAdapter || (isDrizzleDb(config?.db) ? createMikuContestDrizzlePersistenceAdapter(config.db) : null);
9623
+ if (adapter) {
9624
+ return createMikuContestPersistentService({
9625
+ persistenceAdapter: adapter
9626
+ });
9627
+ }
9628
+ return createMikuContestService();
9629
+ };
9630
+ var createGetContestSnapshotHandler = (config) => {
9631
+ const service = resolveService(config);
9632
+ return async (_request) => {
9633
+ const data = await service.getSnapshot();
9634
+ return server.NextResponse.json({ success: true, data });
9635
+ };
9636
+ };
9637
+ var createUpdateContestConfigHandler = (config) => {
9638
+ const service = resolveService(config);
9639
+ return async (request) => {
9640
+ try {
9641
+ const payload = await request.json();
9642
+ const data = await service.updateContestConfig(payload);
9643
+ return server.NextResponse.json({ success: true, data });
9644
+ } catch (error) {
9645
+ return server.NextResponse.json({ success: false, error: error.message }, { status: 400 });
9646
+ }
9647
+ };
9648
+ };
9649
+ var createCreateSubmissionHandler = (config) => {
9650
+ const service = resolveService(config);
9651
+ return async (request) => {
9652
+ try {
9653
+ const body = await request.json();
9654
+ const mode = body.mode || "web";
9655
+ const payload = body.payload;
9656
+ if (!payload) {
9657
+ return server.NextResponse.json({ success: false, error: "payload \u4E0D\u80FD\u4E3A\u7A7A" }, { status: 400 });
9658
+ }
9659
+ const data = await service.createSubmission(payload, mode);
9660
+ return server.NextResponse.json({ success: true, data });
9661
+ } catch (error) {
9662
+ return server.NextResponse.json({ success: false, error: error.message }, { status: 400 });
9663
+ }
9664
+ };
9665
+ };
9666
+ var createVoteHandler = (config) => {
9667
+ const service = resolveService(config);
9668
+ return async (request) => {
9669
+ try {
9670
+ const payload = await request.json();
9671
+ const data = await service.vote(payload);
9672
+ return server.NextResponse.json({ success: true, data });
9673
+ } catch (error) {
9674
+ return server.NextResponse.json({ success: false, error: error.message }, { status: 400 });
9675
+ }
9676
+ };
9677
+ };
9678
+ var createReviewSubmissionHandler = (config) => {
9679
+ const service = resolveService(config);
9680
+ return async (request) => {
9681
+ try {
9682
+ const payload = await request.json();
9683
+ const data = await service.reviewSubmission(payload);
9684
+ return server.NextResponse.json({ success: true, data });
9685
+ } catch (error) {
9686
+ return server.NextResponse.json({ success: false, error: error.message }, { status: 400 });
9687
+ }
9688
+ };
9689
+ };
9690
+ var buildSubmissionFilterFromQuery = (request) => {
9691
+ const search = request.nextUrl.searchParams;
9692
+ const status = search.get("status");
9693
+ const type = search.get("type");
9694
+ return {
9695
+ status: status ? status : void 0,
9696
+ type: type ? type : void 0,
9697
+ authorId: search.get("authorId") || void 0,
9698
+ authorKeyword: search.get("authorKeyword") || void 0,
9699
+ titleKeyword: search.get("titleKeyword") || void 0
9700
+ };
9701
+ };
9702
+ var createListSubmissionsHandler = (config) => {
9703
+ const service = resolveService(config);
9704
+ return async (request) => {
9705
+ const filter = buildSubmissionFilterFromQuery(request);
9706
+ const data = await service.listSubmissions(filter);
9707
+ return server.NextResponse.json({ success: true, data });
9708
+ };
9709
+ };
9710
+ var createSetVoterRestrictionHandler = (config) => {
9711
+ const service = resolveService(config);
9712
+ return async (request) => {
9713
+ try {
9714
+ const payload = await request.json();
9715
+ const data = await service.setVoterRestriction(payload);
9716
+ return server.NextResponse.json({ success: true, data });
9717
+ } catch (error) {
9718
+ return server.NextResponse.json({ success: false, error: error.message }, { status: 400 });
9719
+ }
9720
+ };
9721
+ };
9722
+ var createResetVotesHandler = (config) => {
9723
+ const service = resolveService(config);
9724
+ return async (request) => {
9725
+ try {
9726
+ const payload = await request.json();
9727
+ const data = await service.resetVotes(payload);
9728
+ return server.NextResponse.json({ success: true, data });
9729
+ } catch (error) {
9730
+ return server.NextResponse.json({ success: false, error: error.message }, { status: 400 });
9731
+ }
9732
+ };
9733
+ };
9734
+ var createExportSubmissionsHandler = (config) => {
9735
+ const service = resolveService(config);
9736
+ return async (request) => {
9737
+ const filter = buildSubmissionFilterFromQuery(request);
9738
+ const data = await service.exportSubmissionExcel(filter);
9739
+ const body = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
9740
+ return new server.NextResponse(body, {
9741
+ status: 200,
9742
+ headers: {
9743
+ "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
9744
+ "Content-Disposition": 'attachment; filename="miku-submissions.xlsx"'
9745
+ }
9746
+ });
9747
+ };
9748
+ };
9749
+
9750
+ // src/mikuContest/ui/web/index.ts
9751
+ var web_exports2 = {};
9752
+ __export(web_exports2, {
9753
+ MikuContestAdminPage: () => MikuContestAdminPage_default,
9754
+ MikuContestArtistPage: () => MikuContestArtistPage_default,
9755
+ MikuContestAudiencePage: () => MikuContestAudiencePage_default,
9756
+ MikuContestDashboard: () => MikuContestDashboard_default,
9757
+ MikuContestPage: () => MikuContestPage_default
9758
+ });
9759
+ var MikuContestDashboard = ({ snapshot }) => {
9760
+ return /* @__PURE__ */ React69__namespace.default.createElement("div", null, /* @__PURE__ */ React69__namespace.default.createElement("h2", null, snapshot.contest.name), /* @__PURE__ */ React69__namespace.default.createElement("p", null, snapshot.contest.theme), /* @__PURE__ */ React69__namespace.default.createElement("p", null, "\u6295\u7A3F\u6570\uFF1A", snapshot.submissions.length), /* @__PURE__ */ React69__namespace.default.createElement("p", null, "\u516C\u544A\u6570\uFF1A", snapshot.announcements.length), /* @__PURE__ */ React69__namespace.default.createElement("ul", null, snapshot.leaderboard.map((item) => /* @__PURE__ */ React69__namespace.default.createElement("li", { key: item.submissionId }, "#", item.rank, " ", item.title, " - ", item.voteCount, "\u7968"))));
9761
+ };
9762
+ var MikuContestDashboard_default = MikuContestDashboard;
9763
+ var MikuContestAudiencePage = ({
9764
+ client,
9765
+ voterId,
9766
+ title = "\u89C2\u4F17\u6295\u7968\u533A"
9767
+ }) => {
9768
+ const api = React69.useMemo(() => client || createMikuContestWebClient(), [client]);
9769
+ const [snapshot, setSnapshot] = React69.useState(null);
9770
+ const [loading, setLoading] = React69.useState(false);
9771
+ const [error, setError] = React69.useState(null);
9772
+ const approvedWorks = React69.useMemo(() => {
9773
+ if (!snapshot) return [];
9774
+ return snapshot.submissions.filter((item) => item.status === "approved");
9775
+ }, [snapshot]);
9776
+ const loadSnapshot = async () => {
9777
+ setLoading(true);
9778
+ setError(null);
9779
+ try {
9780
+ const data = await api.getSnapshot();
9781
+ setSnapshot(data);
9782
+ } catch (e) {
9783
+ setError(e.message);
9784
+ } finally {
9785
+ setLoading(false);
9786
+ }
9787
+ };
9788
+ React69.useEffect(() => {
9789
+ void loadSnapshot();
9790
+ }, []);
9791
+ const handleVote = async (submission) => {
9792
+ if (!snapshot) return;
9793
+ try {
9794
+ await api.vote({
9795
+ contestId: snapshot.contest.id,
9796
+ submissionId: submission.id,
9797
+ voterId
9798
+ });
9799
+ await loadSnapshot();
9800
+ } catch (e) {
9801
+ setError(e.message);
9802
+ }
9803
+ };
9804
+ return /* @__PURE__ */ React69__namespace.default.createElement("section", null, /* @__PURE__ */ React69__namespace.default.createElement("h2", null, title), /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => void loadSnapshot(), disabled: loading }, loading ? "\u5237\u65B0\u4E2D..." : "\u5237\u65B0\u6570\u636E"), error ? /* @__PURE__ */ React69__namespace.default.createElement("p", { style: { color: "crimson" } }, "\u9519\u8BEF\uFF1A", error) : null, !snapshot ? null : /* @__PURE__ */ React69__namespace.default.createElement(React69__namespace.default.Fragment, null, /* @__PURE__ */ React69__namespace.default.createElement("p", null, "\u8D5B\u4E8B\uFF1A", snapshot.contest.name, "\uFF5C\u4E3B\u9898\uFF1A", snapshot.contest.theme), /* @__PURE__ */ React69__namespace.default.createElement("p", null, "\u5DF2\u8FC7\u5BA1\u4F5C\u54C1\uFF1A", approvedWorks.length, "\uFF5C\u6BCF\u65E5\u4E0A\u9650\uFF1A", snapshot.contest.votingRules.maxVotesPerDay), /* @__PURE__ */ React69__namespace.default.createElement("ul", null, approvedWorks.map((work) => /* @__PURE__ */ React69__namespace.default.createElement("li", { key: work.id }, /* @__PURE__ */ React69__namespace.default.createElement("strong", null, work.title), "\uFF08", work.authorNickname, "\uFF09- \u5F53\u524D ", work.voteCount, " \u7968", " ", /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => void handleVote(work) }, "\u6295\u7968"))))));
9805
+ };
9806
+ var MikuContestAudiencePage_default = MikuContestAudiencePage;
9807
+ var workTypes = ["visual", "video", "text", "audio"];
9808
+ var MikuContestArtistPage = ({
9809
+ client,
9810
+ authorId,
9811
+ authorNickname,
9812
+ title = "\u753B\u5E08\u6295\u7A3F\u533A"
9813
+ }) => {
9814
+ const api = React69.useMemo(() => client || createMikuContestWebClient(), [client]);
9815
+ const [snapshot, setSnapshot] = React69.useState(null);
9816
+ const [mySubmissions, setMySubmissions] = React69.useState([]);
9817
+ const [submitting, setSubmitting] = React69.useState(false);
9818
+ const [loading, setLoading] = React69.useState(false);
9819
+ const [error, setError] = React69.useState(null);
9820
+ const [titleInput, setTitleInput] = React69.useState("");
9821
+ const [descInput, setDescInput] = React69.useState("");
9822
+ const [coverImage, setCoverImage] = React69.useState("");
9823
+ const [workType, setWorkType] = React69.useState("visual");
9824
+ const loadData = async () => {
9825
+ setLoading(true);
9826
+ setError(null);
9827
+ try {
9828
+ const [contest, mine] = await Promise.all([
9829
+ api.getSnapshot(),
9830
+ api.listSubmissions({ authorId })
9831
+ ]);
9832
+ setSnapshot(contest);
9833
+ setMySubmissions(mine);
9834
+ } catch (e) {
9835
+ setError(e.message);
9836
+ } finally {
9837
+ setLoading(false);
9838
+ }
9839
+ };
9840
+ React69.useEffect(() => {
9841
+ void loadData();
9842
+ }, []);
9843
+ const submitWork = async () => {
9844
+ if (!snapshot) return;
9845
+ const payload = {
9846
+ contestId: snapshot.contest.id,
9847
+ authorId,
9848
+ authorNickname,
9849
+ title: titleInput,
9850
+ description: descInput,
9851
+ type: workType,
9852
+ tags: ["web"],
9853
+ content: {
9854
+ coverImage,
9855
+ images: coverImage ? [coverImage] : void 0
9856
+ }
9857
+ };
9858
+ setSubmitting(true);
9859
+ setError(null);
9860
+ try {
9861
+ await api.createSubmission(payload, "web");
9862
+ setTitleInput("");
9863
+ setDescInput("");
9864
+ setCoverImage("");
9865
+ await loadData();
9866
+ } catch (e) {
9867
+ setError(e.message);
9868
+ } finally {
9869
+ setSubmitting(false);
9870
+ }
9871
+ };
9872
+ return /* @__PURE__ */ React69__namespace.default.createElement("section", null, /* @__PURE__ */ React69__namespace.default.createElement("h2", null, title), /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => void loadData(), disabled: loading }, loading ? "\u5237\u65B0\u4E2D..." : "\u5237\u65B0\u6570\u636E"), error ? /* @__PURE__ */ React69__namespace.default.createElement("p", { style: { color: "crimson" } }, "\u9519\u8BEF\uFF1A", error) : null, /* @__PURE__ */ React69__namespace.default.createElement("div", null, /* @__PURE__ */ React69__namespace.default.createElement("h3", null, "\u65B0\u5EFA\u6295\u7A3F"), /* @__PURE__ */ React69__namespace.default.createElement("input", { value: titleInput, onChange: (e) => setTitleInput(e.target.value), placeholder: "\u4F5C\u54C1\u6807\u9898" }), /* @__PURE__ */ React69__namespace.default.createElement("br", null), /* @__PURE__ */ React69__namespace.default.createElement("textarea", { value: descInput, onChange: (e) => setDescInput(e.target.value), placeholder: "\u4F5C\u54C1\u7B80\u4ECB" }), /* @__PURE__ */ React69__namespace.default.createElement("br", null), /* @__PURE__ */ React69__namespace.default.createElement("input", { value: coverImage, onChange: (e) => setCoverImage(e.target.value), placeholder: "\u5C01\u9762 URL" }), /* @__PURE__ */ React69__namespace.default.createElement("br", null), /* @__PURE__ */ React69__namespace.default.createElement("select", { value: workType, onChange: (e) => setWorkType(e.target.value) }, workTypes.map((item) => /* @__PURE__ */ React69__namespace.default.createElement("option", { value: item, key: item }, item))), /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => void submitWork(), disabled: submitting || !snapshot }, submitting ? "\u63D0\u4EA4\u4E2D..." : "\u63D0\u4EA4\u7A3F\u4EF6")), /* @__PURE__ */ React69__namespace.default.createElement("div", null, /* @__PURE__ */ React69__namespace.default.createElement("h3", null, "\u6211\u7684\u6295\u7A3F\uFF08", mySubmissions.length, "\uFF09"), /* @__PURE__ */ React69__namespace.default.createElement("ul", null, mySubmissions.map((item) => /* @__PURE__ */ React69__namespace.default.createElement("li", { key: item.id }, item.title, "\uFF5C\u72B6\u6001\uFF1A", item.status, "\uFF5C\u7968\u6570\uFF1A", item.voteCount, item.rejectReason ? `\uFF5C\u9A73\u56DE\uFF1A${item.rejectReason}` : "")))));
9873
+ };
9874
+ var MikuContestArtistPage_default = MikuContestArtistPage;
9875
+ var MikuContestAdminPage = ({
9876
+ client,
9877
+ adminId,
9878
+ title = "\u7BA1\u7406\u5458\u9762\u677F"
9879
+ }) => {
9880
+ const api = React69.useMemo(() => client || createMikuContestWebClient(), [client]);
9881
+ const [snapshot, setSnapshot] = React69.useState(null);
9882
+ const [submissions, setSubmissions] = React69.useState([]);
9883
+ const [loading, setLoading] = React69.useState(false);
9884
+ const [error, setError] = React69.useState(null);
9885
+ const [voterId, setVoterId] = React69.useState("");
9886
+ const loadData = async () => {
9887
+ setLoading(true);
9888
+ setError(null);
9889
+ try {
9890
+ const [contest, list] = await Promise.all([api.getSnapshot(), api.listSubmissions()]);
9891
+ setSnapshot(contest);
9892
+ setSubmissions(list);
9893
+ } catch (e) {
9894
+ setError(e.message);
9895
+ } finally {
9896
+ setLoading(false);
9897
+ }
9898
+ };
9899
+ React69.useEffect(() => {
9900
+ void loadData();
9901
+ }, []);
9902
+ const review = async (item, action) => {
9903
+ try {
9904
+ await api.reviewSubmission({
9905
+ submissionId: item.id,
9906
+ reviewerId: adminId,
9907
+ action,
9908
+ rejectReason: action === "reject" ? "\u7BA1\u7406\u5458\u9A73\u56DE" : void 0
9909
+ });
9910
+ await loadData();
9911
+ } catch (e) {
9912
+ setError(e.message);
9913
+ }
9914
+ };
9915
+ const toggleVoting = async (enabled) => {
9916
+ if (!snapshot) return;
9917
+ try {
9918
+ await api.updateContestConfig({
9919
+ toggles: {
9920
+ ...snapshot.contest.toggles,
9921
+ votingEnabled: enabled
9922
+ }
9923
+ });
9924
+ await loadData();
9925
+ } catch (e) {
9926
+ setError(e.message);
9927
+ }
9928
+ };
9929
+ const setRestriction = async (banned) => {
9930
+ if (!snapshot || !voterId.trim()) return;
9931
+ try {
9932
+ await api.setVoterRestriction({
9933
+ voterId: voterId.trim(),
9934
+ banned,
9935
+ reason: banned ? "\u7BA1\u7406\u5458\u624B\u52A8\u5C01\u7981" : "\u7BA1\u7406\u5458\u89E3\u9664\u5C01\u7981",
9936
+ operatorId: adminId
9937
+ });
9938
+ setVoterId("");
9939
+ } catch (e) {
9940
+ setError(e.message);
9941
+ }
9942
+ };
9943
+ const resetVotesByVoter = async () => {
9944
+ if (!voterId.trim()) return;
9945
+ try {
9946
+ await api.resetVotes({ voterId: voterId.trim() });
9947
+ setVoterId("");
9948
+ await loadData();
9949
+ } catch (e) {
9950
+ setError(e.message);
9951
+ }
9952
+ };
9953
+ const exportExcel = async () => {
9954
+ try {
9955
+ const data = await api.exportSubmissions();
9956
+ const blob = new Blob([data], {
9957
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
9958
+ });
9959
+ const url = URL.createObjectURL(blob);
9960
+ const a = document.createElement("a");
9961
+ a.href = url;
9962
+ a.download = "miku-submissions.xlsx";
9963
+ a.click();
9964
+ URL.revokeObjectURL(url);
9965
+ } catch (e) {
9966
+ setError(e.message);
9967
+ }
9968
+ };
9969
+ return /* @__PURE__ */ React69__namespace.default.createElement("section", null, /* @__PURE__ */ React69__namespace.default.createElement("h2", null, title), /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => void loadData(), disabled: loading }, loading ? "\u5237\u65B0\u4E2D..." : "\u5237\u65B0\u6570\u636E"), error ? /* @__PURE__ */ React69__namespace.default.createElement("p", { style: { color: "crimson" } }, "\u9519\u8BEF\uFF1A", error) : null, /* @__PURE__ */ React69__namespace.default.createElement("div", null, /* @__PURE__ */ React69__namespace.default.createElement("h3", null, "\u8D5B\u4E8B\u5F00\u5173"), /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => void toggleVoting(true), disabled: !snapshot }, "\u5F00\u542F\u6295\u7968"), /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => void toggleVoting(false), disabled: !snapshot }, "\u5173\u95ED\u6295\u7968")), /* @__PURE__ */ React69__namespace.default.createElement("div", null, /* @__PURE__ */ React69__namespace.default.createElement("h3", null, "\u6295\u7A3F\u5BA1\u6838\uFF08", submissions.length, "\uFF09"), /* @__PURE__ */ React69__namespace.default.createElement("ul", null, submissions.map((item) => /* @__PURE__ */ React69__namespace.default.createElement("li", { key: item.id }, /* @__PURE__ */ React69__namespace.default.createElement("strong", null, item.title), "\uFF5C\u4F5C\u8005\uFF1A", item.authorNickname, "\uFF5C\u72B6\u6001\uFF1A", item.status, "\uFF5C\u7968\u6570\uFF1A", item.voteCount, item.status === "pending" ? /* @__PURE__ */ React69__namespace.default.createElement(React69__namespace.default.Fragment, null, " ", /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => void review(item, "approve") }, "\u901A\u8FC7"), /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => void review(item, "reject") }, "\u9A73\u56DE")) : null)))), /* @__PURE__ */ React69__namespace.default.createElement("div", null, /* @__PURE__ */ React69__namespace.default.createElement("h3", null, "\u6295\u7968\u98CE\u63A7"), /* @__PURE__ */ React69__namespace.default.createElement("input", { value: voterId, onChange: (e) => setVoterId(e.target.value), placeholder: "voterId" }), /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => void setRestriction(true), disabled: !snapshot }, "\u5C01\u7981\u6295\u7968"), /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => void setRestriction(false), disabled: !snapshot }, "\u89E3\u9664\u5C01\u7981"), /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => void resetVotesByVoter() }, "\u6E05\u96F6\u8BE5\u7528\u6237\u7968\u6570")), /* @__PURE__ */ React69__namespace.default.createElement("div", null, /* @__PURE__ */ React69__namespace.default.createElement("h3", null, "\u5BFC\u51FA"), /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => void exportExcel() }, "\u5BFC\u51FA\u6295\u7A3F Excel")));
9970
+ };
9971
+ var MikuContestAdminPage_default = MikuContestAdminPage;
9972
+
9973
+ // src/mikuContest/ui/web/pages/MikuContestPage.tsx
9974
+ var MikuContestPage = ({
9975
+ defaultView = "audience",
9976
+ viewerVoterId = "viewer-demo",
9977
+ artistId = "artist-demo",
9978
+ artistNickname = "Demo \u753B\u5E08",
9979
+ adminId = "admin-demo"
9980
+ }) => {
9981
+ const [view, setView] = React69.useState(defaultView);
9982
+ return /* @__PURE__ */ React69__namespace.default.createElement("div", null, /* @__PURE__ */ React69__namespace.default.createElement("h1", null, "Miku Contest"), /* @__PURE__ */ React69__namespace.default.createElement("div", null, /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => setView("audience") }, "\u89C2\u4F17\u7AEF"), /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => setView("artist") }, "\u753B\u5E08\u7AEF"), /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => setView("admin") }, "\u7BA1\u7406\u5458\u7AEF")), view === "audience" ? /* @__PURE__ */ React69__namespace.default.createElement(MikuContestAudiencePage_default, { voterId: viewerVoterId }) : null, view === "artist" ? /* @__PURE__ */ React69__namespace.default.createElement(MikuContestArtistPage_default, { authorId: artistId, authorNickname: artistNickname }) : null, view === "admin" ? /* @__PURE__ */ React69__namespace.default.createElement(MikuContestAdminPage_default, { adminId }) : null);
9983
+ };
9984
+ var MikuContestPage_default = MikuContestPage;
9985
+
9986
+ // src/mikuContest/ui/miniapp/index.ts
9987
+ var miniapp_exports2 = {};
9988
+ __export(miniapp_exports2, {
9989
+ MikuContestMiniappHome: () => MikuContestMiniappHome_default,
9990
+ MikuContestMiniappPage: () => MikuContestMiniappPage_default
9991
+ });
9992
+ var MikuContestMiniappHome = ({ snapshot }) => {
9993
+ return /* @__PURE__ */ React69__namespace.default.createElement("div", null, /* @__PURE__ */ React69__namespace.default.createElement("h3", null, snapshot.contest.name), /* @__PURE__ */ React69__namespace.default.createElement("p", null, "\u6295\u7A3F\uFF1A", snapshot.submissions.length, " | \u516C\u544A\uFF1A", snapshot.announcements.length), /* @__PURE__ */ React69__namespace.default.createElement("ol", null, snapshot.leaderboard.slice(0, 3).map((item) => /* @__PURE__ */ React69__namespace.default.createElement("li", { key: item.submissionId }, item.title, "\uFF08", item.voteCount, "\u7968\uFF09"))));
9994
+ };
9995
+ var MikuContestMiniappHome_default = MikuContestMiniappHome;
9996
+ var MikuContestMiniappPage = () => {
9997
+ const { service, snapshot, refresh } = useMikuContest();
9998
+ const [tab, setTab] = React69.useState("vote");
9999
+ const [voterId, setVoterId] = React69.useState("miniapp-voter");
10000
+ const [authorId, setAuthorId] = React69.useState("miniapp-author");
10001
+ const [authorNickname, setAuthorNickname] = React69.useState("\u5C0F\u7A0B\u5E8F\u753B\u5E08");
10002
+ const [title, setTitle] = React69.useState("");
10003
+ const [desc, setDesc] = React69.useState("");
10004
+ const [type, setType] = React69.useState("visual");
10005
+ const [error, setError] = React69.useState(null);
10006
+ const approvedWorks = React69.useMemo(() => {
10007
+ return snapshot.submissions.filter((item) => item.status === "approved");
10008
+ }, [snapshot.submissions]);
10009
+ const vote = (submissionId) => {
10010
+ try {
10011
+ service.vote({
10012
+ contestId: snapshot.contest.id,
10013
+ submissionId,
10014
+ voterId
10015
+ });
10016
+ refresh();
10017
+ setError(null);
10018
+ } catch (e) {
10019
+ setError(e.message);
10020
+ }
10021
+ };
10022
+ const submit = () => {
10023
+ try {
10024
+ service.createSubmission(
10025
+ {
10026
+ contestId: snapshot.contest.id,
10027
+ authorId,
10028
+ authorNickname,
10029
+ title,
10030
+ description: desc,
10031
+ type,
10032
+ content: {}
10033
+ },
10034
+ "miniapp"
10035
+ );
10036
+ setTitle("");
10037
+ setDesc("");
10038
+ refresh();
10039
+ setError(null);
10040
+ } catch (e) {
10041
+ setError(e.message);
10042
+ }
10043
+ };
10044
+ return /* @__PURE__ */ React69__namespace.default.createElement("div", null, /* @__PURE__ */ React69__namespace.default.createElement("h3", null, snapshot.contest.name), /* @__PURE__ */ React69__namespace.default.createElement("p", null, "\u5C0F\u7A0B\u5E8F\u7AEF\u793A\u4F8B\u9875\u9762"), /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => setTab("vote") }, "\u89C2\u4F17\u6295\u7968"), /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => setTab("submit") }, "\u753B\u5E08\u6295\u7A3F"), error ? /* @__PURE__ */ React69__namespace.default.createElement("p", { style: { color: "crimson" } }, "\u9519\u8BEF\uFF1A", error) : null, tab === "vote" ? /* @__PURE__ */ React69__namespace.default.createElement("div", null, /* @__PURE__ */ React69__namespace.default.createElement("input", { value: voterId, onChange: (e) => setVoterId(e.target.value), placeholder: "voterId" }), /* @__PURE__ */ React69__namespace.default.createElement("ul", null, approvedWorks.map((item) => /* @__PURE__ */ React69__namespace.default.createElement("li", { key: item.id }, item.title, "\uFF08", item.voteCount, "\u7968\uFF09", /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: () => vote(item.id) }, "\u6295\u7968"))))) : null, tab === "submit" ? /* @__PURE__ */ React69__namespace.default.createElement("div", null, /* @__PURE__ */ React69__namespace.default.createElement("input", { value: authorId, onChange: (e) => setAuthorId(e.target.value), placeholder: "authorId" }), /* @__PURE__ */ React69__namespace.default.createElement("input", { value: authorNickname, onChange: (e) => setAuthorNickname(e.target.value), placeholder: "\u4F5C\u8005\u6635\u79F0" }), /* @__PURE__ */ React69__namespace.default.createElement("input", { value: title, onChange: (e) => setTitle(e.target.value), placeholder: "\u4F5C\u54C1\u6807\u9898" }), /* @__PURE__ */ React69__namespace.default.createElement("input", { value: desc, onChange: (e) => setDesc(e.target.value), placeholder: "\u4F5C\u54C1\u7B80\u4ECB" }), /* @__PURE__ */ React69__namespace.default.createElement("select", { value: type, onChange: (e) => setType(e.target.value) }, /* @__PURE__ */ React69__namespace.default.createElement("option", { value: "visual" }, "visual"), /* @__PURE__ */ React69__namespace.default.createElement("option", { value: "video" }, "video"), /* @__PURE__ */ React69__namespace.default.createElement("option", { value: "text" }, "text"), /* @__PURE__ */ React69__namespace.default.createElement("option", { value: "audio" }, "audio")), /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: submit }, "\u63D0\u4EA4")) : null);
10045
+ };
10046
+ var MikuContestMiniappPage_default = MikuContestMiniappPage;
10047
+
10048
+ // src/huarongdao/index.ts
10049
+ var huarongdao_exports = {};
10050
+ __export(huarongdao_exports, {
10051
+ HuarongdaoService: () => HuarongdaoService,
10052
+ buildSolvedTiles: () => buildSolvedTiles,
10053
+ canMove: () => canMove,
10054
+ createCreateConfigHandler: () => createCreateConfigHandler,
10055
+ createDeleteConfigHandler: () => createDeleteConfigHandler,
10056
+ createGetSnapshotHandler: () => createGetSnapshotHandler,
10057
+ createHuarongdaoApiClient: () => createHuarongdaoApiClient,
10058
+ createHuarongdaoService: () => createHuarongdaoService,
10059
+ createHuarongdaoWebClient: () => createHuarongdaoWebClient,
10060
+ createListConfigsHandler: () => createListConfigsHandler,
10061
+ createUpdateConfigHandler: () => createUpdateConfigHandler,
10062
+ inversionCount: () => inversionCount,
10063
+ isSolvable: () => isSolvable,
10064
+ isSolved: () => isSolved,
10065
+ moveTile: () => moveTile,
10066
+ shuffleSolvable: () => shuffleSolvable,
10067
+ webUI: () => web_exports3
10068
+ });
10069
+
10070
+ // src/huarongdao/logic/game.ts
10071
+ var clone = (v) => [...v];
10072
+ var buildSolvedTiles = (rows, cols) => {
10073
+ const total = rows * cols;
10074
+ return Array.from({ length: total }, (_, i) => (i + 1) % total);
10075
+ };
10076
+ var isSolved = (tiles) => {
10077
+ return tiles.every((v, i, arr) => v === (i + 1) % arr.length);
10078
+ };
10079
+ var canMove = (tiles, rows, cols, tileIndex) => {
10080
+ const blank = tiles.indexOf(0);
10081
+ if (blank < 0 || tileIndex < 0 || tileIndex >= tiles.length) return false;
10082
+ const br = Math.floor(blank / cols);
10083
+ const bc = blank % cols;
10084
+ const tr = Math.floor(tileIndex / cols);
10085
+ const tc = tileIndex % cols;
10086
+ return Math.abs(br - tr) + Math.abs(bc - tc) === 1;
10087
+ };
10088
+ var moveTile = (state, tileIndex) => {
10089
+ if (!canMove(state.tiles, state.rows, state.cols, tileIndex)) return state;
10090
+ const nextTiles = clone(state.tiles);
10091
+ const blank = nextTiles.indexOf(0);
10092
+ const blankValue = nextTiles[blank];
10093
+ const tileValue = nextTiles[tileIndex];
10094
+ if (blankValue === void 0 || tileValue === void 0) return state;
10095
+ nextTiles[blank] = tileValue;
10096
+ nextTiles[tileIndex] = blankValue;
10097
+ const solved = isSolved(nextTiles);
10098
+ return {
10099
+ ...state,
10100
+ tiles: nextTiles,
10101
+ moveCount: state.moveCount + 1,
10102
+ isSolved: solved,
10103
+ finishedAt: solved ? Date.now() : void 0
10104
+ };
10105
+ };
10106
+ var inversionCount = (tiles) => {
10107
+ const arr = tiles.filter((n) => n !== 0);
10108
+ let cnt = 0;
10109
+ for (let i = 0; i < arr.length; i += 1) {
10110
+ for (let j = i + 1; j < arr.length; j += 1) {
10111
+ if ((arr[i] ?? 0) > (arr[j] ?? 0)) cnt += 1;
10112
+ }
10113
+ }
10114
+ return cnt;
10115
+ };
10116
+ var isSolvable = (tiles, rows, cols) => {
10117
+ const inv = inversionCount(tiles);
10118
+ if (cols % 2 === 1) return inv % 2 === 0;
10119
+ const blankRowFromBottom = rows - Math.floor(tiles.indexOf(0) / cols);
10120
+ return blankRowFromBottom % 2 === 0 !== (inv % 2 === 0);
10121
+ };
10122
+ var shuffleSolvable = (rows, cols, steps = 80) => {
10123
+ let tiles = buildSolvedTiles(rows, cols);
10124
+ for (let i = 0; i < steps; i += 1) {
10125
+ const blank = tiles.indexOf(0);
10126
+ const br = Math.floor(blank / cols);
10127
+ const bc = blank % cols;
10128
+ const candidate = [];
10129
+ const dirs = [
10130
+ [1, 0],
10131
+ [-1, 0],
10132
+ [0, 1],
10133
+ [0, -1]
10134
+ ];
10135
+ dirs.forEach(([dr, dc]) => {
10136
+ const nr = br + dr;
10137
+ const nc = bc + dc;
10138
+ if (nr >= 0 && nr < rows && nc >= 0 && nc < cols) candidate.push(nr * cols + nc);
10139
+ });
10140
+ const idx = candidate[Math.floor(Math.random() * candidate.length)] ?? blank;
10141
+ const next = clone(tiles);
10142
+ [next[blank], next[idx]] = [next[idx] ?? 0, next[blank] ?? 0];
10143
+ tiles = next;
10144
+ }
10145
+ return tiles;
10146
+ };
10147
+
10148
+ // src/huarongdao/service/api/client.ts
10149
+ var unwrap2 = (result) => {
10150
+ if (!result.success || result.data === void 0) throw new Error(result.error || "\u8BF7\u6C42\u5931\u8D25");
10151
+ return result.data;
10152
+ };
10153
+ var createHuarongdaoApiClient = (basePath, requester2) => ({
10154
+ async getSnapshot() {
10155
+ return unwrap2(await requester2(`${basePath}/snapshot`, { method: "GET" }));
10156
+ },
10157
+ async listConfigs() {
10158
+ return unwrap2(await requester2(`${basePath}/configs`, { method: "GET" }));
10159
+ },
10160
+ async createConfig(input) {
10161
+ return unwrap2(await requester2(`${basePath}/configs`, { method: "POST", body: input }));
10162
+ },
10163
+ async updateConfig(id2, patch) {
10164
+ return unwrap2(await requester2(`${basePath}/configs`, { method: "PATCH", body: { id: id2, patch } }));
10165
+ },
10166
+ async deleteConfig(id2) {
10167
+ return unwrap2(await requester2(`${basePath}/configs`, { method: "DELETE", body: { id: id2 } }));
10168
+ }
10169
+ });
10170
+
10171
+ // src/huarongdao/service/web/client.ts
10172
+ var requester = (options) => {
10173
+ const baseUrl = options.baseUrl || "";
10174
+ return async (url, req) => {
10175
+ const res = await fetch(`${baseUrl}${url}`, {
10176
+ method: req?.method || "GET",
10177
+ headers: { "Content-Type": "application/json", ...options.headers || {} },
10178
+ body: req?.body ? JSON.stringify(req.body) : void 0
10179
+ });
10180
+ return await res.json();
10181
+ };
10182
+ };
10183
+ var createHuarongdaoWebClient = (options = {}) => {
10184
+ return createHuarongdaoApiClient(options.basePath || "/api/huarongdao", requester(options));
10185
+ };
10186
+
10187
+ // src/huarongdao/server/service.ts
10188
+ var id = () => `puzzle_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
10189
+ var HuarongdaoService = class {
10190
+ constructor() {
10191
+ this.configs = /* @__PURE__ */ new Map();
10192
+ }
10193
+ listConfigs() {
10194
+ return [...this.configs.values()];
10195
+ }
10196
+ getBySlug(slug) {
10197
+ return [...this.configs.values()].find((c) => c.slug === slug) || null;
10198
+ }
10199
+ createConfig(input) {
10200
+ const now = (/* @__PURE__ */ new Date()).toISOString();
10201
+ const next = {
10202
+ id: id(),
10203
+ slug: input.slug,
10204
+ name: input.name,
10205
+ description: input.description,
10206
+ status: "draft",
10207
+ rows: input.rows,
10208
+ cols: input.cols,
10209
+ sourceImageUrl: input.sourceImageUrl,
10210
+ showReference: input.showReference ?? true,
10211
+ shuffleSteps: input.shuffleSteps ?? 80,
10212
+ timeLimitSec: input.timeLimitSec,
10213
+ startMode: input.startMode ?? "random-solvable",
10214
+ initialTiles: input.initialTiles,
10215
+ createdAt: now,
10216
+ updatedAt: now
10217
+ };
10218
+ this.configs.set(next.id, next);
10219
+ return next;
10220
+ }
10221
+ updateConfig(id2, patch) {
10222
+ const cur = this.configs.get(id2);
10223
+ if (!cur) throw new Error("\u914D\u7F6E\u4E0D\u5B58\u5728");
10224
+ const next = { ...cur, ...patch, id: cur.id, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
10225
+ this.configs.set(id2, next);
10226
+ return next;
10227
+ }
10228
+ deleteConfig(id2) {
10229
+ this.configs.delete(id2);
10230
+ }
10231
+ getSnapshot() {
10232
+ const configs = this.listConfigs();
10233
+ return {
10234
+ configs,
10235
+ activeConfig: configs.find((c) => c.status === "active")
10236
+ };
10237
+ }
10238
+ };
10239
+ var createHuarongdaoService = () => new HuarongdaoService();
10240
+ var resolveService2 = (config) => config?.service || createHuarongdaoService();
10241
+ var createGetSnapshotHandler = (config) => {
10242
+ const service = resolveService2(config);
10243
+ return async (_req) => server.NextResponse.json({ success: true, data: service.getSnapshot() });
10244
+ };
10245
+ var createListConfigsHandler = (config) => {
10246
+ const service = resolveService2(config);
10247
+ return async (_req) => server.NextResponse.json({ success: true, data: service.listConfigs() });
10248
+ };
10249
+ var createCreateConfigHandler = (config) => {
10250
+ const service = resolveService2(config);
10251
+ return async (req) => {
10252
+ try {
10253
+ const body = await req.json();
10254
+ const data = service.createConfig(body);
10255
+ return server.NextResponse.json({ success: true, data });
10256
+ } catch (e) {
10257
+ return server.NextResponse.json({ success: false, error: e.message }, { status: 400 });
10258
+ }
10259
+ };
10260
+ };
10261
+ var createUpdateConfigHandler = (config) => {
10262
+ const service = resolveService2(config);
10263
+ return async (req) => {
10264
+ try {
10265
+ const body = await req.json();
10266
+ const data = service.updateConfig(body.id, body.patch || {});
10267
+ return server.NextResponse.json({ success: true, data });
10268
+ } catch (e) {
10269
+ return server.NextResponse.json({ success: false, error: e.message }, { status: 400 });
10270
+ }
10271
+ };
10272
+ };
10273
+ var createDeleteConfigHandler = (config) => {
10274
+ const service = resolveService2(config);
10275
+ return async (req) => {
10276
+ try {
10277
+ const body = await req.json();
10278
+ service.deleteConfig(body.id);
10279
+ return server.NextResponse.json({ success: true, data: true });
10280
+ } catch (e) {
10281
+ return server.NextResponse.json({ success: false, error: e.message }, { status: 400 });
10282
+ }
10283
+ };
10284
+ };
10285
+
10286
+ // src/huarongdao/ui/web/index.ts
10287
+ var web_exports3 = {};
10288
+ __export(web_exports3, {
10289
+ HuarongdaoBoard: () => HuarongdaoBoard_default,
10290
+ HuarongdaoConfigPage: () => HuarongdaoConfigPage_default,
10291
+ HuarongdaoGamePage: () => HuarongdaoGamePage_default
10292
+ });
10293
+ var HuarongdaoBoard = ({ tiles, rows, cols, imageUrl, onClickTile }) => {
10294
+ const total = rows * cols;
10295
+ return /* @__PURE__ */ React69__namespace.default.createElement(
10296
+ "div",
10297
+ {
10298
+ style: {
10299
+ display: "grid",
10300
+ gridTemplateColumns: `repeat(${cols}, 96px)`,
10301
+ gridTemplateRows: `repeat(${rows}, 96px)`,
10302
+ gap: 6
10303
+ }
10304
+ },
10305
+ tiles.map((tile, index) => {
10306
+ if (tile === 0) return /* @__PURE__ */ React69__namespace.default.createElement("div", { key: `blank-${index}`, style: { background: "#ddd" } });
10307
+ const pieceIndex = tile - 1;
10308
+ const pr = Math.floor(pieceIndex / cols);
10309
+ const pc = pieceIndex % cols;
10310
+ return /* @__PURE__ */ React69__namespace.default.createElement(
10311
+ "button",
10312
+ {
10313
+ key: `${tile}-${index}`,
10314
+ onClick: () => onClickTile(index),
10315
+ style: {
10316
+ border: "1px solid #666",
10317
+ cursor: "pointer",
10318
+ backgroundImage: `url(${imageUrl})`,
10319
+ backgroundSize: `${cols * 96}px ${rows * 96}px`,
10320
+ backgroundPosition: `${-pc * 96}px ${-pr * 96}px`
10321
+ }
10322
+ }
10323
+ );
10324
+ }),
10325
+ tiles.length !== total ? /* @__PURE__ */ React69__namespace.default.createElement("div", null, "tiles invalid") : null
10326
+ );
10327
+ };
10328
+ var HuarongdaoBoard_default = HuarongdaoBoard;
10329
+ var HuarongdaoGamePage = ({ config }) => {
10330
+ const initial = React69.useMemo(() => {
10331
+ const tiles = config.startMode === "custom-layout" && config.initialTiles?.length === config.rows * config.cols ? config.initialTiles : shuffleSolvable(config.rows, config.cols, config.shuffleSteps);
10332
+ return {
10333
+ tiles,
10334
+ rows: config.rows,
10335
+ cols: config.cols,
10336
+ moveCount: 0,
10337
+ startedAt: Date.now(),
10338
+ isSolved: false
10339
+ };
10340
+ }, [config]);
10341
+ const [state, setState] = React69.useState(initial);
10342
+ const reset = () => {
10343
+ setState({
10344
+ ...state,
10345
+ tiles: shuffleSolvable(config.rows, config.cols, config.shuffleSteps),
10346
+ moveCount: 0,
10347
+ startedAt: Date.now(),
10348
+ finishedAt: void 0,
10349
+ isSolved: false
10350
+ });
10351
+ };
10352
+ const solveNow = () => {
10353
+ setState((prev) => ({ ...prev, tiles: buildSolvedTiles(config.rows, config.cols), isSolved: true, finishedAt: Date.now() }));
10354
+ };
10355
+ const durationSec = Math.floor(((state.finishedAt || Date.now()) - state.startedAt) / 1e3);
10356
+ return /* @__PURE__ */ React69__namespace.default.createElement("section", null, /* @__PURE__ */ React69__namespace.default.createElement("h2", null, config.name), /* @__PURE__ */ React69__namespace.default.createElement("p", null, "\u6B65\u6570\uFF1A", state.moveCount, " \uFF5C \u7528\u65F6\uFF1A", durationSec, "s ", state.isSolved ? "\uFF5C\u5DF2\u901A\u5173 \u{1F389}" : ""), /* @__PURE__ */ React69__namespace.default.createElement(
10357
+ HuarongdaoBoard_default,
10358
+ {
10359
+ tiles: state.tiles,
10360
+ rows: state.rows,
10361
+ cols: state.cols,
10362
+ imageUrl: config.sourceImageUrl,
10363
+ onClickTile: (idx) => setState((prev) => moveTile(prev, idx))
10364
+ }
10365
+ ), /* @__PURE__ */ React69__namespace.default.createElement("div", { style: { marginTop: 12 } }, /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: reset }, "\u91CD\u65B0\u6253\u4E71"), /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: solveNow }, "\u4E00\u952E\u5B8C\u6210(\u6D4B\u8BD5)")), config.showReference ? /* @__PURE__ */ React69__namespace.default.createElement("div", { style: { marginTop: 12 } }, /* @__PURE__ */ React69__namespace.default.createElement("div", null, "\u53C2\u8003\u56FE\uFF1A"), /* @__PURE__ */ React69__namespace.default.createElement("img", { src: config.sourceImageUrl, alt: "reference", style: { width: 180, border: "1px solid #ccc" } })) : null);
10366
+ };
10367
+ var HuarongdaoGamePage_default = HuarongdaoGamePage;
10368
+ var HuarongdaoConfigPage = () => {
10369
+ const service = React69.useMemo(() => createHuarongdaoService(), []);
10370
+ const [version, setVersion] = React69.useState(0);
10371
+ const [name, setName] = React69.useState("\u793A\u4F8B\u534E\u5BB9\u9053");
10372
+ const [slug, setSlug] = React69.useState("sample-huarongdao");
10373
+ const [imageUrl, setImageUrl] = React69.useState("https://i.imgur.com/6z7Qw6M.png");
10374
+ const [rows, setRows] = React69.useState(3);
10375
+ const [cols, setCols] = React69.useState(3);
10376
+ const list = React69.useMemo(() => {
10377
+ return service.listConfigs();
10378
+ }, [service, version]);
10379
+ const create = () => {
10380
+ const input = {
10381
+ name,
10382
+ slug,
10383
+ sourceImageUrl: imageUrl,
10384
+ rows,
10385
+ cols,
10386
+ showReference: true,
10387
+ shuffleSteps: 80
10388
+ };
10389
+ service.createConfig(input);
10390
+ setVersion((v) => v + 1);
10391
+ };
10392
+ return /* @__PURE__ */ React69__namespace.default.createElement("section", null, /* @__PURE__ */ React69__namespace.default.createElement("h2", null, "\u534E\u5BB9\u9053\u540E\u53F0\u914D\u7F6E\u9875"), /* @__PURE__ */ React69__namespace.default.createElement("p", null, "\u652F\u6301\u591A\u5957\u914D\u7F6E\u521B\u5EFA\u3001\u53C2\u6570\u5316\u7BA1\u7406\uFF08V1 \u9AA8\u67B6\uFF09"), /* @__PURE__ */ React69__namespace.default.createElement("div", null, /* @__PURE__ */ React69__namespace.default.createElement("input", { value: name, onChange: (e) => setName(e.target.value), placeholder: "name" }), /* @__PURE__ */ React69__namespace.default.createElement("input", { value: slug, onChange: (e) => setSlug(e.target.value), placeholder: "slug" }), /* @__PURE__ */ React69__namespace.default.createElement("input", { value: imageUrl, onChange: (e) => setImageUrl(e.target.value), placeholder: "image url" }), /* @__PURE__ */ React69__namespace.default.createElement("input", { type: "number", value: rows, onChange: (e) => setRows(Number(e.target.value) || 3) }), /* @__PURE__ */ React69__namespace.default.createElement("input", { type: "number", value: cols, onChange: (e) => setCols(Number(e.target.value) || 3) }), /* @__PURE__ */ React69__namespace.default.createElement("button", { onClick: create }, "\u521B\u5EFA\u914D\u7F6E")), /* @__PURE__ */ React69__namespace.default.createElement("ul", null, list.map((item) => /* @__PURE__ */ React69__namespace.default.createElement("li", { key: item.id }, item.name, " (", item.slug, ") - ", item.rows, "x", item.cols, " - ", item.status))));
10393
+ };
10394
+ var HuarongdaoConfigPage_default = HuarongdaoConfigPage;
10395
+
8802
10396
  // src/storage/adapters/react-native-adapter.ts
8803
10397
  var AsyncStorage = null;
8804
10398
  try {
@@ -9252,6 +10846,9 @@ exports.LocalImageMappingPanel = LocalImageMappingPanel;
9252
10846
  exports.LogLevel = LogLevel;
9253
10847
  exports.Logger = Logger;
9254
10848
  exports.MIKU_PALETTE = MIKU_PALETTE;
10849
+ exports.MikuContestPersistentService = MikuContestPersistentService;
10850
+ exports.MikuContestService = MikuContestService;
10851
+ exports.MikuContestStateDbService = MikuContestStateDbService;
9255
10852
  exports.MikuFireworks3D = MikuFireworks3D;
9256
10853
  exports.Modal = Modal;
9257
10854
  exports.NORMAL_PALETTE = NORMAL_PALETTE;
@@ -9316,14 +10913,30 @@ exports.applyPromptTemplate = applyPromptTemplate;
9316
10913
  exports.arrayUtils = arrayUtils;
9317
10914
  exports.badgeVariants = badgeVariants;
9318
10915
  exports.buttonVariants = buttonVariants;
10916
+ exports.checkVoteEligibility = checkVoteEligibility;
9319
10917
  exports.cn = cn;
9320
10918
  exports.createAiClient = createAiClient;
9321
10919
  exports.createChatSession = createChatSession;
10920
+ exports.createCreateSubmissionHandler = createCreateSubmissionHandler;
10921
+ exports.createDefaultMikuContestConfig = createDefaultMikuContestConfig;
10922
+ exports.createExportSubmissionsHandler = createExportSubmissionsHandler;
10923
+ exports.createGetContestSnapshotHandler = createGetContestSnapshotHandler;
9322
10924
  exports.createInMemoryFestivalCardDb = createInMemoryFestivalCardDb;
10925
+ exports.createListSubmissionsHandler = createListSubmissionsHandler;
9323
10926
  exports.createLogger = createLogger;
10927
+ exports.createMikuContestApiClient = createMikuContestApiClient;
10928
+ exports.createMikuContestDrizzlePersistenceAdapter = createMikuContestDrizzlePersistenceAdapter;
10929
+ exports.createMikuContestPersistentService = createMikuContestPersistentService;
10930
+ exports.createMikuContestService = createMikuContestService;
9324
10931
  exports.createOpenAICompatibleProvider = createOpenAICompatibleProvider;
10932
+ exports.createResetVotesHandler = createResetVotesHandler;
10933
+ exports.createReviewSubmissionHandler = createReviewSubmissionHandler;
10934
+ exports.createSetVoterRestrictionHandler = createSetVoterRestrictionHandler;
9325
10935
  exports.createSkillRegistry = createSkillRegistry;
10936
+ exports.createUpdateContestConfigHandler = createUpdateContestConfigHandler;
10937
+ exports.createVoteHandler = createVoteHandler;
9326
10938
  exports.debugUtils = debugUtils;
10939
+ exports.defaultMikuVotingRules = defaultMikuVotingRules;
9327
10940
  exports.defaultVocaloidBoothConfig = defaultVocaloidBoothConfig;
9328
10941
  exports.errorUtils = errorUtils;
9329
10942
  exports.fileUtils = fileUtils;
@@ -9337,8 +10950,17 @@ exports.getCompletionFilterDisplayName = getCompletionFilterDisplayName;
9337
10950
  exports.getCompletionStatusColor = getCompletionStatusColor;
9338
10951
  exports.getCompletionStatusText = getCompletionStatusText;
9339
10952
  exports.getExperimentCounts = getExperimentCounts;
10953
+ exports.huarongdao = huarongdao_exports;
9340
10954
  exports.japaneseUtils = japaneseUtils;
9341
10955
  exports.logger = logger;
10956
+ exports.mikuContestConfigs = mikuContestConfigs;
10957
+ exports.mikuContestDbService = mikuContestDbService;
10958
+ exports.mikuContestNotices = mikuContestNotices;
10959
+ exports.mikuContestSubmissions = mikuContestSubmissions;
10960
+ exports.mikuContestVoterRestrictions = mikuContestVoterRestrictions;
10961
+ exports.mikuContestVotes = mikuContestVotes;
10962
+ exports.miniappService = miniapp_exports;
10963
+ exports.miniappUI = miniapp_exports2;
9342
10964
  exports.normalizeFestivalCardConfig = normalizeFestivalCardConfig;
9343
10965
  exports.normalizeMatchCode = normalizeMatchCode;
9344
10966
  exports.normalizePromptVariables = normalizePromptVariables;
@@ -9346,8 +10968,10 @@ exports.normalizeVocaloidBoothConfig = normalizeVocaloidBoothConfig;
9346
10968
  exports.resizeFestivalCardPages = resizeFestivalCardPages;
9347
10969
  exports.resolveScreenReceiverSignalUrl = resolveScreenReceiverSignalUrl;
9348
10970
  exports.skillToToolDefinition = skillToToolDefinition;
10971
+ exports.sortByVotesDesc = sortByVotesDesc;
9349
10972
  exports.sortExperiments = sortExperiments;
9350
10973
  exports.stringUtils = stringUtils;
10974
+ exports.toVoteDayKey = toVoteDayKey;
9351
10975
  exports.useAiChat = useAiChat;
9352
10976
  exports.useAsyncStorage = useAsyncStorage;
9353
10977
  exports.useBackgroundRemoval = useBackgroundRemoval;
@@ -9357,6 +10981,7 @@ exports.useFestivalCardConfig = useFestivalCardConfig;
9357
10981
  exports.useFireworksEngine = useFireworksEngine;
9358
10982
  exports.useFireworksRealtime = useFireworksRealtime;
9359
10983
  exports.useLocalStorage = useLocalStorage;
10984
+ exports.useMikuContest = useMikuContest;
9360
10985
  exports.useOCR = useOCR;
9361
10986
  exports.useScreenReceiver = useScreenReceiver;
9362
10987
  exports.useSentimentAnalysis = useSentimentAnalysis;
@@ -9364,6 +10989,9 @@ exports.useStorage = useStorage;
9364
10989
  exports.useTaroStorage = useTaroStorage;
9365
10990
  exports.useTextGeneration = useTextGeneration;
9366
10991
  exports.validateExperiment = validateExperiment;
10992
+ exports.validateMikuSubmissionInput = validateMikuSubmissionInput;
9367
10993
  exports.validators = validators;
10994
+ exports.webService = web_exports;
10995
+ exports.webUI = web_exports2;
9368
10996
  //# sourceMappingURL=index.js.map
9369
10997
  //# sourceMappingURL=index.js.map