sa2kit 3.2.0 → 3.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/dist/CollisionBalls-DgKtscU2.d.mts +41 -0
  2. package/dist/CollisionBalls-DgKtscU2.d.ts +41 -0
  3. package/dist/ConfigService-Oga_zFRS.d.mts +262 -0
  4. package/dist/ConfigService-Oga_zFRS.d.ts +262 -0
  5. package/dist/UniversalFileService-CC4d3wkc.d.ts +139 -0
  6. package/dist/UniversalFileService-CzAE_G4V.d.mts +139 -0
  7. package/dist/boothVaultService-lKcnyA-u.d.mts +83 -0
  8. package/dist/boothVaultService-lKcnyA-u.d.ts +83 -0
  9. package/dist/business/audioDetection/index.d.mts +2 -0
  10. package/dist/business/audioDetection/index.d.ts +2 -0
  11. package/dist/business/audioDetection/index.js +1244 -0
  12. package/dist/business/audioDetection/index.js.map +1 -0
  13. package/dist/business/audioDetection/index.mjs +1227 -0
  14. package/dist/business/audioDetection/index.mjs.map +1 -0
  15. package/dist/business/calendar/index.d.mts +6 -0
  16. package/dist/business/calendar/index.d.ts +6 -0
  17. package/dist/business/calendar/index.js +7433 -0
  18. package/dist/business/calendar/index.js.map +1 -0
  19. package/dist/business/calendar/index.mjs +7257 -0
  20. package/dist/business/calendar/index.mjs.map +1 -0
  21. package/dist/business/calendar/routes/index.d.mts +191 -0
  22. package/dist/business/calendar/routes/index.d.ts +191 -0
  23. package/dist/business/calendar/routes/index.js +844 -0
  24. package/dist/business/calendar/routes/index.js.map +1 -0
  25. package/dist/business/calendar/routes/index.mjs +826 -0
  26. package/dist/business/calendar/routes/index.mjs.map +1 -0
  27. package/dist/business/festivalCard/index.d.mts +4 -0
  28. package/dist/business/festivalCard/index.d.ts +4 -0
  29. package/dist/business/festivalCard/index.js +1492 -0
  30. package/dist/business/festivalCard/index.js.map +1 -0
  31. package/dist/business/festivalCard/index.mjs +1475 -0
  32. package/dist/business/festivalCard/index.mjs.map +1 -0
  33. package/dist/business/festivalCard/routes/index.d.mts +42 -0
  34. package/dist/business/festivalCard/routes/index.d.ts +42 -0
  35. package/dist/business/festivalCard/routes/index.js +361 -0
  36. package/dist/business/festivalCard/routes/index.js.map +1 -0
  37. package/dist/business/festivalCard/routes/index.mjs +356 -0
  38. package/dist/business/festivalCard/routes/index.mjs.map +1 -0
  39. package/dist/business/festivalCard/server/index.d.mts +120 -0
  40. package/dist/business/festivalCard/server/index.d.ts +120 -0
  41. package/dist/business/festivalCard/server/index.js +272 -0
  42. package/dist/business/festivalCard/server/index.js.map +1 -0
  43. package/dist/business/festivalCard/server/index.mjs +265 -0
  44. package/dist/business/festivalCard/server/index.mjs.map +1 -0
  45. package/dist/business/index.d.mts +34 -0
  46. package/dist/business/index.d.ts +34 -0
  47. package/dist/business/index.js +29282 -0
  48. package/dist/business/index.js.map +1 -0
  49. package/dist/business/index.mjs +29237 -0
  50. package/dist/business/index.mjs.map +1 -0
  51. package/dist/business/mikuContest/ui/web/index.d.mts +2 -0
  52. package/dist/business/mikuContest/ui/web/index.d.ts +2 -0
  53. package/dist/business/mikuContest/ui/web/index.js +353 -0
  54. package/dist/business/mikuContest/ui/web/index.js.map +1 -0
  55. package/dist/business/mikuContest/ui/web/index.mjs +343 -0
  56. package/dist/business/mikuContest/ui/web/index.mjs.map +1 -0
  57. package/dist/business/mikuFireworks3D/index.d.mts +2 -0
  58. package/dist/business/mikuFireworks3D/index.d.ts +2 -0
  59. package/dist/business/mikuFireworks3D/index.js +1267 -0
  60. package/dist/business/mikuFireworks3D/index.js.map +1 -0
  61. package/dist/business/mikuFireworks3D/index.mjs +1228 -0
  62. package/dist/business/mikuFireworks3D/index.mjs.map +1 -0
  63. package/dist/business/mikuFusionGame/index.d.mts +2 -0
  64. package/dist/business/mikuFusionGame/index.d.ts +2 -0
  65. package/dist/business/mikuFusionGame/index.js +1208 -0
  66. package/dist/business/mikuFusionGame/index.js.map +1 -0
  67. package/dist/business/mikuFusionGame/index.mjs +1195 -0
  68. package/dist/business/mikuFusionGame/index.mjs.map +1 -0
  69. package/dist/business/mmd/admin/index.d.mts +487 -0
  70. package/dist/business/mmd/admin/index.d.ts +487 -0
  71. package/dist/business/mmd/admin/index.js +1058 -0
  72. package/dist/business/mmd/admin/index.js.map +1 -0
  73. package/dist/business/mmd/admin/index.mjs +1027 -0
  74. package/dist/business/mmd/admin/index.mjs.map +1 -0
  75. package/dist/business/mmd/index.d.mts +5 -0
  76. package/dist/business/mmd/index.d.ts +5 -0
  77. package/dist/business/mmd/index.js +10119 -0
  78. package/dist/business/mmd/index.js.map +1 -0
  79. package/dist/business/mmd/index.mjs +10028 -0
  80. package/dist/business/mmd/index.mjs.map +1 -0
  81. package/dist/business/mmd/server/index.d.mts +139 -0
  82. package/dist/business/mmd/server/index.d.ts +139 -0
  83. package/dist/business/mmd/server/index.js +424 -0
  84. package/dist/business/mmd/server/index.js.map +1 -0
  85. package/dist/business/mmd/server/index.mjs +404 -0
  86. package/dist/business/mmd/server/index.mjs.map +1 -0
  87. package/dist/business/music/index.d.mts +3 -0
  88. package/dist/business/music/index.d.ts +3 -0
  89. package/dist/business/music/index.js +830 -0
  90. package/dist/business/music/index.js.map +1 -0
  91. package/dist/business/music/index.mjs +809 -0
  92. package/dist/business/music/index.mjs.map +1 -0
  93. package/dist/business/music/server/index.d.mts +1 -0
  94. package/dist/business/music/server/index.d.ts +1 -0
  95. package/dist/business/music/server/index.js +194 -0
  96. package/dist/business/music/server/index.js.map +1 -0
  97. package/dist/business/music/server/index.mjs +182 -0
  98. package/dist/business/music/server/index.mjs.map +1 -0
  99. package/dist/business/navigation/index.d.mts +2 -0
  100. package/dist/business/navigation/index.d.ts +2 -0
  101. package/dist/business/navigation/index.js +453 -0
  102. package/dist/business/navigation/index.js.map +1 -0
  103. package/dist/business/navigation/index.mjs +443 -0
  104. package/dist/business/navigation/index.mjs.map +1 -0
  105. package/dist/business/portfolio/index.d.mts +3 -0
  106. package/dist/business/portfolio/index.d.ts +3 -0
  107. package/dist/business/portfolio/index.js +736 -0
  108. package/dist/business/portfolio/index.js.map +1 -0
  109. package/dist/business/portfolio/index.mjs +724 -0
  110. package/dist/business/portfolio/index.mjs.map +1 -0
  111. package/dist/business/qqbot/server/index.d.mts +167 -0
  112. package/dist/business/qqbot/server/index.d.ts +167 -0
  113. package/dist/business/qqbot/server/index.js +394 -0
  114. package/dist/business/qqbot/server/index.js.map +1 -0
  115. package/dist/business/qqbot/server/index.mjs +385 -0
  116. package/dist/business/qqbot/server/index.mjs.map +1 -0
  117. package/dist/business/qqbot/ui/web/index.d.mts +10 -0
  118. package/dist/business/qqbot/ui/web/index.d.ts +10 -0
  119. package/dist/business/qqbot/ui/web/index.js +105 -0
  120. package/dist/business/qqbot/ui/web/index.js.map +1 -0
  121. package/dist/business/qqbot/ui/web/index.mjs +99 -0
  122. package/dist/business/qqbot/ui/web/index.mjs.map +1 -0
  123. package/dist/business/screenReceiver/index.d.mts +2 -0
  124. package/dist/business/screenReceiver/index.d.ts +2 -0
  125. package/dist/business/screenReceiver/index.js +281 -0
  126. package/dist/business/screenReceiver/index.js.map +1 -0
  127. package/dist/business/screenReceiver/index.mjs +273 -0
  128. package/dist/business/screenReceiver/index.mjs.map +1 -0
  129. package/dist/business/testYourself/admin/index.d.mts +58 -0
  130. package/dist/business/testYourself/admin/index.d.ts +58 -0
  131. package/dist/business/testYourself/admin/index.js +1009 -0
  132. package/dist/business/testYourself/admin/index.js.map +1 -0
  133. package/dist/business/testYourself/admin/index.mjs +1002 -0
  134. package/dist/business/testYourself/admin/index.mjs.map +1 -0
  135. package/dist/business/testYourself/index.d.mts +6 -0
  136. package/dist/business/testYourself/index.d.ts +6 -0
  137. package/dist/business/testYourself/index.js +2551 -0
  138. package/dist/business/testYourself/index.js.map +1 -0
  139. package/dist/business/testYourself/index.mjs +2531 -0
  140. package/dist/business/testYourself/index.mjs.map +1 -0
  141. package/dist/business/testYourself/server/index.d.mts +1029 -0
  142. package/dist/business/testYourself/server/index.d.ts +1029 -0
  143. package/dist/business/testYourself/server/index.js +825 -0
  144. package/dist/business/testYourself/server/index.js.map +1 -0
  145. package/dist/business/testYourself/server/index.mjs +816 -0
  146. package/dist/business/testYourself/server/index.mjs.map +1 -0
  147. package/dist/business/vocaloidBooth/index.d.mts +2 -0
  148. package/dist/business/vocaloidBooth/index.d.ts +2 -0
  149. package/dist/business/vocaloidBooth/index.js +172 -0
  150. package/dist/business/vocaloidBooth/index.js.map +1 -0
  151. package/dist/business/vocaloidBooth/index.mjs +166 -0
  152. package/dist/business/vocaloidBooth/index.mjs.map +1 -0
  153. package/dist/business/vocaloidBooth/server/index.d.mts +111 -0
  154. package/dist/business/vocaloidBooth/server/index.d.ts +111 -0
  155. package/dist/business/vocaloidBooth/server/index.js +247 -0
  156. package/dist/business/vocaloidBooth/server/index.js.map +1 -0
  157. package/dist/business/vocaloidBooth/server/index.mjs +237 -0
  158. package/dist/business/vocaloidBooth/server/index.mjs.map +1 -0
  159. package/dist/business/vocaloidBooth/web/index.d.mts +45 -0
  160. package/dist/business/vocaloidBooth/web/index.d.ts +45 -0
  161. package/dist/business/vocaloidBooth/web/index.js +376 -0
  162. package/dist/business/vocaloidBooth/web/index.js.map +1 -0
  163. package/dist/business/vocaloidBooth/web/index.mjs +362 -0
  164. package/dist/business/vocaloidBooth/web/index.mjs.map +1 -0
  165. package/dist/common/auth/components/index.js +18 -0
  166. package/dist/common/auth/components/index.js.map +1 -1
  167. package/dist/common/auth/components/index.mjs +18 -0
  168. package/dist/common/auth/components/index.mjs.map +1 -1
  169. package/dist/common/auth/server/index.d.mts +86 -3
  170. package/dist/common/auth/server/index.d.ts +86 -3
  171. package/dist/common/auth/server/index.js +614 -1
  172. package/dist/common/auth/server/index.js.map +1 -1
  173. package/dist/common/auth/server/index.mjs +599 -3
  174. package/dist/common/auth/server/index.mjs.map +1 -1
  175. package/dist/config-BQp3qLAL.d.mts +22 -0
  176. package/dist/config-BQp3qLAL.d.ts +22 -0
  177. package/dist/drizzle-schema-BNhqj2AZ.d.mts +1114 -0
  178. package/dist/drizzle-schema-BNhqj2AZ.d.ts +1114 -0
  179. package/dist/festivalCardService-D60G-sgr.d.mts +13 -0
  180. package/dist/festivalCardService-DnLyJpRh.d.ts +13 -0
  181. package/dist/index-BMgdH5dL.d.mts +1716 -0
  182. package/dist/index-BO9_Do5y.d.mts +93 -0
  183. package/dist/index-BO9_Do5y.d.ts +93 -0
  184. package/dist/index-BSmd4ikf.d.ts +76 -0
  185. package/dist/index-BSwvWYp2.d.mts +2632 -0
  186. package/dist/index-Bo_fW3Tl.d.mts +105 -0
  187. package/dist/index-Bo_fW3Tl.d.ts +105 -0
  188. package/dist/index-BrKazb8M.d.mts +148 -0
  189. package/dist/index-BrKazb8M.d.ts +148 -0
  190. package/dist/index-Bzh6QE4P.d.ts +25 -0
  191. package/dist/index-C5Ic6eSR.d.mts +25 -0
  192. package/dist/index-C8i9SIxk.d.ts +2632 -0
  193. package/dist/index-C_GhVhOT.d.mts +109 -0
  194. package/dist/index-C_GhVhOT.d.ts +109 -0
  195. package/dist/index-Cb3UEpG4.d.mts +101 -0
  196. package/dist/index-CjlkUj01.d.mts +103 -0
  197. package/dist/index-CucXCBNR.d.mts +302 -0
  198. package/dist/index-CucXCBNR.d.ts +302 -0
  199. package/dist/index-DLLPTprx.d.mts +1522 -0
  200. package/dist/index-DRiZy0dv.d.mts +525 -0
  201. package/dist/index-DRiZy0dv.d.ts +525 -0
  202. package/dist/index-Dc_I2t0P.d.mts +103 -0
  203. package/dist/index-DowAHRIP.d.mts +250 -0
  204. package/dist/index-DowAHRIP.d.ts +250 -0
  205. package/dist/index-Dpq_5H2n.d.ts +103 -0
  206. package/dist/index-Ds2M_9zb.d.ts +101 -0
  207. package/dist/index-IXMAeTtN.d.ts +1716 -0
  208. package/dist/index-VFDbZxVM.d.ts +1522 -0
  209. package/dist/index-jadkp96n.d.ts +103 -0
  210. package/dist/index-r2-zE3iC.d.mts +76 -0
  211. package/dist/index.d.mts +10682 -0
  212. package/dist/index.d.ts +10682 -0
  213. package/dist/index.js +38233 -0
  214. package/dist/index.js.map +1 -0
  215. package/dist/index.mjs +37959 -0
  216. package/dist/index.mjs.map +1 -0
  217. package/dist/types-B6B210gX.d.mts +270 -0
  218. package/dist/types-B6B210gX.d.ts +270 -0
  219. package/dist/types-B7voqjjA.d.mts +51 -0
  220. package/dist/types-B7voqjjA.d.ts +51 -0
  221. package/dist/types-Bdnte5EN.d.mts +292 -0
  222. package/dist/types-C2z_QQPI.d.mts +70 -0
  223. package/dist/types-C2z_QQPI.d.ts +70 -0
  224. package/dist/types-HorDyIRv.d.mts +303 -0
  225. package/dist/types-HorDyIRv.d.ts +303 -0
  226. package/dist/types-_rFX1atk.d.ts +292 -0
  227. package/package.json +10 -2
@@ -0,0 +1,1058 @@
1
+ 'use strict';
2
+
3
+ var React2 = require('react');
4
+ var clsx = require('clsx');
5
+ var lucideReact = require('lucide-react');
6
+ var JSZip = require('jszip');
7
+ var drizzleOrm = require('drizzle-orm');
8
+ var pgCore = require('drizzle-orm/pg-core');
9
+
10
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
+
12
+ var React2__default = /*#__PURE__*/_interopDefault(React2);
13
+ var JSZip__default = /*#__PURE__*/_interopDefault(JSZip);
14
+
15
+ // src/business/mmd/admin/components/MmdAdminPanel.tsx
16
+
17
+ // src/business/mmd/admin/types.ts
18
+ var MMD_RESOURCE_TYPE_CONFIGS = {
19
+ model: {
20
+ moduleId: "mmd-models",
21
+ acceptedTypes: [".zip"],
22
+ maxFileSize: 150,
23
+ description: "MMD\u6A21\u578B\u538B\u7F29\u5305 (ZIP)",
24
+ hint: "\u8BF7\u4E0A\u4F20\u5305\u542B PMX/\u8D34\u56FE\u7B49\u5B8C\u6574\u76EE\u5F55\u7684 ZIP \u538B\u7F29\u5305\uFF0C\u4FDD\u6301\u539F\u59CB\u6587\u4EF6\u7ED3\u6784"
25
+ },
26
+ motion: {
27
+ moduleId: "mmd-motions",
28
+ acceptedTypes: [".vmd"],
29
+ maxFileSize: 20,
30
+ description: "MMD\u52A8\u4F5C\u6587\u4EF6"
31
+ },
32
+ camera: {
33
+ moduleId: "mmd-cameras",
34
+ acceptedTypes: [".vmd"],
35
+ maxFileSize: 10,
36
+ description: "MMD\u76F8\u673A\u52A8\u753B\u6587\u4EF6"
37
+ },
38
+ audio: {
39
+ moduleId: "mmd-audios",
40
+ acceptedTypes: [".mp3", ".wav", ".ogg", ".m4a"],
41
+ maxFileSize: 20,
42
+ description: "\u97F3\u9891\u6587\u4EF6"
43
+ },
44
+ stage: {
45
+ moduleId: "mmd-stages",
46
+ acceptedTypes: [".zip"],
47
+ maxFileSize: 200,
48
+ description: "\u821E\u53F0/\u573A\u666F\u538B\u7F29\u5305 (ZIP)",
49
+ hint: "\u9700\u5C06\u821E\u53F0\u6A21\u578B\u4E0E\u4F9D\u8D56\u8D34\u56FE\u4E00\u8D77\u6253\u5305 ZIP\uFF0C\u5E76\u4FDD\u6301\u76EE\u5F55\u7ED3\u6784"
50
+ },
51
+ thumbnail: {
52
+ moduleId: "mmd-thumbnails",
53
+ acceptedTypes: [".jpg", ".jpeg", ".png", ".webp"],
54
+ maxFileSize: 5,
55
+ description: "\u7F29\u7565\u56FE"
56
+ }
57
+ };
58
+
59
+ // src/business/mmd/admin/components/MmdResourceSelector.tsx
60
+ var MmdResourceSelector = ({
61
+ resourceType,
62
+ fileService,
63
+ userId,
64
+ value,
65
+ onChange,
66
+ required = false
67
+ }) => {
68
+ const [selectedFileId, setSelectedFileId] = React2.useState(value);
69
+ const [selectedFile, setSelectedFile] = React2.useState(null);
70
+ const [files, setFiles] = React2.useState([]);
71
+ const [loading, setLoading] = React2.useState(false);
72
+ const [uploading, setUploading] = React2.useState(false);
73
+ const [searchTerm, setSearchTerm] = React2.useState("");
74
+ const [showUploader, setShowUploader] = React2.useState(false);
75
+ const [zipValidationError, setZipValidationError] = React2.useState(null);
76
+ const config = MMD_RESOURCE_TYPE_CONFIGS[resourceType];
77
+ if (!config) {
78
+ return /* @__PURE__ */ React2__default.default.createElement("div", { className: "p-4 bg-red-50 text-red-600 rounded-lg" }, "\u672A\u627E\u5230\u8D44\u6E90\u7C7B\u578B\u914D\u7F6E\uFF1A", resourceType);
79
+ }
80
+ const getFileIcon = () => {
81
+ switch (resourceType) {
82
+ case "model":
83
+ case "stage":
84
+ return /* @__PURE__ */ React2__default.default.createElement(lucideReact.Film, { className: "w-5 h-5" });
85
+ case "motion":
86
+ case "camera":
87
+ return /* @__PURE__ */ React2__default.default.createElement(lucideReact.FileText, { className: "w-5 h-5" });
88
+ case "audio":
89
+ return /* @__PURE__ */ React2__default.default.createElement(lucideReact.Music, { className: "w-5 h-5" });
90
+ default:
91
+ return /* @__PURE__ */ React2__default.default.createElement(lucideReact.FileText, { className: "w-5 h-5" });
92
+ }
93
+ };
94
+ const loadFiles = React2.useCallback(async () => {
95
+ if (!fileService || !config) return;
96
+ setLoading(true);
97
+ try {
98
+ const result = await fileService.queryFiles({
99
+ moduleId: config.moduleId,
100
+ pageSize: 50,
101
+ page: 1,
102
+ sortBy: "uploadTime",
103
+ sortOrder: "desc"
104
+ });
105
+ setFiles(result.items || []);
106
+ } catch (error) {
107
+ console.error("\u52A0\u8F7D\u6587\u4EF6\u5217\u8868\u5931\u8D25:", error);
108
+ } finally {
109
+ setLoading(false);
110
+ }
111
+ }, [fileService, config]);
112
+ React2.useEffect(() => {
113
+ loadFiles();
114
+ }, [loadFiles]);
115
+ React2.useEffect(() => {
116
+ if (selectedFileId && fileService) {
117
+ fileService.getFileMetadata(selectedFileId).then((file) => setSelectedFile(file)).catch((error) => console.error("\u52A0\u8F7D\u6587\u4EF6\u4FE1\u606F\u5931\u8D25:", error));
118
+ } else {
119
+ setSelectedFile(null);
120
+ }
121
+ }, [selectedFileId, fileService]);
122
+ const handleFileSelect = (file) => {
123
+ setSelectedFileId(file.id);
124
+ setSelectedFile(file);
125
+ fileService.getFileUrl(file.id).then((url) => onChange(file.id, url)).catch((error) => console.error("\u83B7\u53D6\u6587\u4EF6URL\u5931\u8D25:", error));
126
+ };
127
+ const validateZipContents = async (buffer, type) => {
128
+ const zip = await JSZip__default.default.loadAsync(buffer);
129
+ const entries = Object.keys(zip.files);
130
+ if (!entries.length) {
131
+ throw new Error("\u538B\u7F29\u5305\u4E3A\u7A7A\uFF0C\u8BF7\u68C0\u67E5\u6587\u4EF6\u5185\u5BB9");
132
+ }
133
+ const hasModel = entries.some((name) => /\.[pP][mM][xX]$/.test(name)) || type === "stage" && entries.some((name) => /\.[pP][mM][dD]$/.test(name));
134
+ const hasAssets = entries.some(
135
+ (name) => /\.(png|jpg|jpeg|bmp|tga|dds|spa|sph)$/i.test(name)
136
+ );
137
+ if (!hasModel) {
138
+ throw new Error(type === "stage" ? "\u538B\u7F29\u5305\u4E2D\u672A\u627E\u5230 PMX/PMD \u821E\u53F0\u6A21\u578B\u6587\u4EF6" : "\u538B\u7F29\u5305\u4E2D\u672A\u627E\u5230 PMX \u6A21\u578B\u6587\u4EF6");
139
+ }
140
+ if (!hasAssets) {
141
+ throw new Error("\u538B\u7F29\u5305\u4E2D\u672A\u53D1\u73B0\u8D34\u56FE\u6587\u4EF6\uFF0C\u8BF7\u786E\u8BA4\u662F\u5426\u5305\u542B texture \u76EE\u5F55");
142
+ }
143
+ };
144
+ const handleFileUpload = async (file) => {
145
+ if (!fileService) return;
146
+ setUploading(true);
147
+ setZipValidationError(null);
148
+ try {
149
+ if (resourceType === "model" || resourceType === "stage") {
150
+ const arrayBuffer = await file.arrayBuffer();
151
+ await validateZipContents(arrayBuffer, resourceType);
152
+ }
153
+ const fileMetadata = await fileService.uploadFile({
154
+ file,
155
+ moduleId: config.moduleId,
156
+ businessId: userId,
157
+ permission: "public"
158
+ });
159
+ await loadFiles();
160
+ handleFileSelect(fileMetadata);
161
+ setShowUploader(false);
162
+ } catch (error) {
163
+ console.error("\u6587\u4EF6\u4E0A\u4F20\u5931\u8D25:", error);
164
+ const message = error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF";
165
+ setZipValidationError(message);
166
+ alert("\u4E0A\u4F20\u5931\u8D25: " + message);
167
+ } finally {
168
+ setUploading(false);
169
+ }
170
+ };
171
+ const filteredFiles = files.filter(
172
+ (file) => file.originalName.toLowerCase().includes(searchTerm.toLowerCase())
173
+ );
174
+ return /* @__PURE__ */ React2__default.default.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React2__default.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React2__default.default.createElement("label", { className: "flex items-center gap-2 text-sm font-medium text-gray-700 dark:text-gray-300" }, getFileIcon(), config.description, required && /* @__PURE__ */ React2__default.default.createElement("span", { className: "text-red-500" }, "*")), /* @__PURE__ */ React2__default.default.createElement(
175
+ "button",
176
+ {
177
+ onClick: () => setShowUploader(!showUploader),
178
+ className: "px-3 py-1 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center gap-2"
179
+ },
180
+ /* @__PURE__ */ React2__default.default.createElement(lucideReact.Upload, { className: "w-4 h-4" }),
181
+ "\u4E0A\u4F20\u65B0\u6587\u4EF6"
182
+ )), selectedFile && /* @__PURE__ */ React2__default.default.createElement("div", { className: "p-3 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg flex items-center justify-between" }, /* @__PURE__ */ React2__default.default.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React2__default.default.createElement(lucideReact.CheckCircle2, { className: "w-5 h-5 text-green-600 dark:text-green-400" }), /* @__PURE__ */ React2__default.default.createElement("div", null, /* @__PURE__ */ React2__default.default.createElement("div", { className: "text-sm font-medium text-gray-900 dark:text-white" }, selectedFile.originalName), /* @__PURE__ */ React2__default.default.createElement("div", { className: "text-xs text-gray-500 dark:text-gray-400" }, (selectedFile.size / 1024 / 1024).toFixed(2), " MB"))), /* @__PURE__ */ React2__default.default.createElement(
183
+ "button",
184
+ {
185
+ onClick: () => {
186
+ setSelectedFileId(void 0);
187
+ setSelectedFile(null);
188
+ onChange("", "");
189
+ },
190
+ className: "p-1 hover:bg-white/50 dark:hover:bg-black/20 rounded transition-colors"
191
+ },
192
+ /* @__PURE__ */ React2__default.default.createElement(lucideReact.X, { className: "w-4 h-4" })
193
+ )), showUploader && /* @__PURE__ */ React2__default.default.createElement("div", { className: "p-4 bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg" }, /* @__PURE__ */ React2__default.default.createElement("div", { className: "text-sm text-gray-600 dark:text-gray-400 mb-3 space-y-1" }, /* @__PURE__ */ React2__default.default.createElement("p", null, "\u652F\u6301\u7684\u6587\u4EF6\u7C7B\u578B: ", config.acceptedTypes.join(", ")), /* @__PURE__ */ React2__default.default.createElement("p", null, "\u6700\u5927\u6587\u4EF6\u5927\u5C0F: ", config.maxFileSize, "MB"), config.hint && /* @__PURE__ */ React2__default.default.createElement("p", { className: "text-xs text-amber-600 dark:text-amber-400" }, config.hint)), /* @__PURE__ */ React2__default.default.createElement(
194
+ "input",
195
+ {
196
+ type: "file",
197
+ accept: config.acceptedTypes.join(","),
198
+ onChange: (e) => {
199
+ const file = e.target.files?.[0];
200
+ if (file) {
201
+ if (file.size > config.maxFileSize * 1024 * 1024) {
202
+ alert("\u6587\u4EF6\u5927\u5C0F\u8D85\u8FC7\u9650\u5236\uFF08\u6700\u5927 " + config.maxFileSize + "MB\uFF09");
203
+ return;
204
+ }
205
+ handleFileUpload(file);
206
+ }
207
+ },
208
+ disabled: uploading,
209
+ className: "w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100 disabled:opacity-50"
210
+ }
211
+ ), uploading && /* @__PURE__ */ React2__default.default.createElement("div", { className: "mt-3 flex items-center gap-2 text-sm text-blue-600" }, /* @__PURE__ */ React2__default.default.createElement(lucideReact.Loader2, { className: "w-4 h-4 animate-spin" }), "\u4E0A\u4F20\u4E2D..."), zipValidationError && /* @__PURE__ */ React2__default.default.createElement("div", { className: "mt-3 flex items-center gap-2 text-sm text-amber-600 dark:text-amber-400" }, /* @__PURE__ */ React2__default.default.createElement(lucideReact.AlertTriangle, { className: "w-4 h-4" }), zipValidationError)), /* @__PURE__ */ React2__default.default.createElement("div", { className: "relative" }, /* @__PURE__ */ React2__default.default.createElement(lucideReact.Search, { className: "absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" }), /* @__PURE__ */ React2__default.default.createElement(
212
+ "input",
213
+ {
214
+ type: "text",
215
+ placeholder: "\u641C\u7D22\u6587\u4EF6...",
216
+ value: searchTerm,
217
+ onChange: (e) => setSearchTerm(e.target.value),
218
+ className: "w-full pl-10 pr-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
219
+ }
220
+ )), /* @__PURE__ */ React2__default.default.createElement("div", { className: "max-h-96 overflow-y-auto border border-gray-200 dark:border-gray-700 rounded-lg divide-y divide-gray-200 dark:divide-gray-700" }, loading ? /* @__PURE__ */ React2__default.default.createElement("div", { className: "p-8 text-center text-gray-500" }, /* @__PURE__ */ React2__default.default.createElement(lucideReact.Loader2, { className: "w-6 h-6 animate-spin mx-auto mb-2" }), "\u52A0\u8F7D\u4E2D...") : filteredFiles.length === 0 ? /* @__PURE__ */ React2__default.default.createElement("div", { className: "p-8 text-center text-gray-500" }, searchTerm ? "\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u6587\u4EF6" : "\u6682\u65E0\u6587\u4EF6\uFF0C\u8BF7\u4E0A\u4F20") : filteredFiles.map((file) => /* @__PURE__ */ React2__default.default.createElement(
221
+ "button",
222
+ {
223
+ key: file.id,
224
+ onClick: () => handleFileSelect(file),
225
+ className: clsx.clsx("w-full p-3 text-left hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors", selectedFileId === file.id ? "bg-blue-50 dark:bg-blue-900/20" : "")
226
+ },
227
+ /* @__PURE__ */ React2__default.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React2__default.default.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React2__default.default.createElement("div", { className: "text-sm font-medium text-gray-900 dark:text-white truncate" }, file.originalName), /* @__PURE__ */ React2__default.default.createElement("div", { className: "text-xs text-gray-500 dark:text-gray-400 mt-0.5" }, (file.size / 1024 / 1024).toFixed(2), " MB \u2022", " ", new Date(file.uploadTime).toLocaleDateString())), selectedFileId === file.id && /* @__PURE__ */ React2__default.default.createElement(lucideReact.CheckCircle2, { className: "w-5 h-5 text-blue-600 dark:text-blue-400 flex-shrink-0 ml-2" }))
228
+ ))), required && !selectedFileId && /* @__PURE__ */ React2__default.default.createElement("div", { className: "text-sm text-red-600 dark:text-red-400" }, "\u8BF7\u9009\u62E9\u4E00\u4E2A", config.description));
229
+ };
230
+
231
+ // src/business/mmd/admin/components/MmdPlaylistEditor.tsx
232
+ var MmdPlaylistEditor = ({
233
+ playlistId,
234
+ fileService,
235
+ userId,
236
+ onSave,
237
+ onCancel
238
+ }) => {
239
+ const [playlistName, setPlaylistName] = React2.useState("");
240
+ const [playlistDescription, setPlaylistDescription] = React2.useState("");
241
+ const [loop, setLoop] = React2.useState(false);
242
+ const [autoPlay, setAutoPlay] = React2.useState(false);
243
+ const [preloadStrategy, setPreloadStrategy] = React2.useState("none");
244
+ const [nodes, setNodes] = React2.useState([]);
245
+ const [expandedNodes, setExpandedNodes] = React2.useState(/* @__PURE__ */ new Set([0]));
246
+ const [saving, setSaving] = React2.useState(false);
247
+ const [showAdvanced, setShowAdvanced] = React2.useState(false);
248
+ const addNode = () => {
249
+ const newNode = {
250
+ name: "\u8282\u70B9 " + (nodes.length + 1),
251
+ description: "",
252
+ loop: false,
253
+ sortOrder: nodes.length,
254
+ modelFileId: ""
255
+ };
256
+ setNodes([...nodes, newNode]);
257
+ setExpandedNodes(/* @__PURE__ */ new Set([...expandedNodes, nodes.length]));
258
+ };
259
+ const removeNode = (index2) => {
260
+ const newNodes = nodes.filter((_, i) => i !== index2);
261
+ newNodes.forEach((node, i) => {
262
+ node.sortOrder = i;
263
+ });
264
+ setNodes(newNodes);
265
+ const newExpanded = /* @__PURE__ */ new Set();
266
+ expandedNodes.forEach((i) => {
267
+ if (i < index2) newExpanded.add(i);
268
+ else if (i > index2) newExpanded.add(i - 1);
269
+ });
270
+ setExpandedNodes(newExpanded);
271
+ };
272
+ const moveNode = (index2, direction) => {
273
+ const newNodes = [...nodes];
274
+ const targetIndex = direction === "up" ? index2 - 1 : index2 + 1;
275
+ if (targetIndex < 0 || targetIndex >= newNodes.length) return;
276
+ if (!newNodes[index2] || !newNodes[targetIndex]) return;
277
+ [newNodes[index2], newNodes[targetIndex]] = [newNodes[targetIndex], newNodes[index2]];
278
+ newNodes.forEach((node, i) => {
279
+ node.sortOrder = i;
280
+ });
281
+ setNodes(newNodes);
282
+ const newExpanded = /* @__PURE__ */ new Set();
283
+ expandedNodes.forEach((i) => {
284
+ if (i === index2) newExpanded.add(targetIndex);
285
+ else if (i === targetIndex) newExpanded.add(index2);
286
+ else newExpanded.add(i);
287
+ });
288
+ setExpandedNodes(newExpanded);
289
+ };
290
+ const toggleNode = (index2) => {
291
+ const newExpanded = new Set(expandedNodes);
292
+ if (newExpanded.has(index2)) {
293
+ newExpanded.delete(index2);
294
+ } else {
295
+ newExpanded.add(index2);
296
+ }
297
+ setExpandedNodes(newExpanded);
298
+ };
299
+ const updateNode = (index2, updates) => {
300
+ const newNodes = [...nodes];
301
+ if (!newNodes[index2]) return;
302
+ newNodes[index2] = { ...newNodes[index2], ...updates };
303
+ setNodes(newNodes);
304
+ };
305
+ const validateForm = () => {
306
+ if (!playlistName.trim()) {
307
+ alert("\u8BF7\u8F93\u5165\u64AD\u653E\u5217\u8868\u540D\u79F0");
308
+ return false;
309
+ }
310
+ if (nodes.length === 0) {
311
+ alert("\u8BF7\u81F3\u5C11\u6DFB\u52A0\u4E00\u4E2A\u64AD\u653E\u8282\u70B9");
312
+ return false;
313
+ }
314
+ for (let i = 0; i < nodes.length; i++) {
315
+ const node = nodes[i];
316
+ if (!node || !node.name?.trim()) {
317
+ alert("\u8282\u70B9 " + (i + 1) + ": \u8BF7\u8F93\u5165\u8282\u70B9\u540D\u79F0");
318
+ return false;
319
+ }
320
+ if (!node.modelFileId) {
321
+ alert("\u8282\u70B9 " + (i + 1) + ": \u8BF7\u9009\u62E9\u6A21\u578B\u6587\u4EF6");
322
+ return false;
323
+ }
324
+ }
325
+ return true;
326
+ };
327
+ const handleSave = async () => {
328
+ if (!validateForm()) return;
329
+ setSaving(true);
330
+ try {
331
+ console.log("\u4FDD\u5B58\u64AD\u653E\u5217\u8868:", {
332
+ name: playlistName,
333
+ description: playlistDescription,
334
+ loop,
335
+ autoPlay,
336
+ preloadStrategy,
337
+ nodes
338
+ });
339
+ alert("\u4FDD\u5B58\u6210\u529F\uFF01");
340
+ onSave?.(null);
341
+ } catch (error) {
342
+ console.error("\u4FDD\u5B58\u5931\u8D25:", error);
343
+ alert("\u4FDD\u5B58\u5931\u8D25: " + (error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"));
344
+ } finally {
345
+ setSaving(false);
346
+ }
347
+ };
348
+ return /* @__PURE__ */ React2__default.default.createElement("div", { className: "max-w-6xl mx-auto p-6 bg-white dark:bg-gray-900 rounded-lg shadow-lg" }, /* @__PURE__ */ React2__default.default.createElement("div", { className: "flex items-center justify-between mb-6" }, /* @__PURE__ */ React2__default.default.createElement("h2", { className: "text-2xl font-bold text-gray-900 dark:text-white" }, playlistId ? "\u7F16\u8F91\u64AD\u653E\u5217\u8868" : "\u521B\u5EFA\u64AD\u653E\u5217\u8868"), /* @__PURE__ */ React2__default.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React2__default.default.createElement(
349
+ "button",
350
+ {
351
+ onClick: handleSave,
352
+ disabled: saving,
353
+ className: "px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50 flex items-center gap-2"
354
+ },
355
+ /* @__PURE__ */ React2__default.default.createElement(lucideReact.Save, { className: "w-4 h-4" }),
356
+ saving ? "\u4FDD\u5B58\u4E2D..." : "\u4FDD\u5B58"
357
+ ), /* @__PURE__ */ React2__default.default.createElement(
358
+ "button",
359
+ {
360
+ onClick: onCancel,
361
+ className: "px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors flex items-center gap-2"
362
+ },
363
+ /* @__PURE__ */ React2__default.default.createElement(lucideReact.X, { className: "w-4 h-4" }),
364
+ "\u53D6\u6D88"
365
+ ))), /* @__PURE__ */ React2__default.default.createElement("div", { className: "space-y-4 mb-8 p-6 bg-gray-50 dark:bg-gray-800 rounded-lg" }, /* @__PURE__ */ React2__default.default.createElement("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white mb-4" }, "\u57FA\u672C\u4FE1\u606F"), /* @__PURE__ */ React2__default.default.createElement("div", null, /* @__PURE__ */ React2__default.default.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u64AD\u653E\u5217\u8868\u540D\u79F0 ", /* @__PURE__ */ React2__default.default.createElement("span", { className: "text-red-500" }, "*")), /* @__PURE__ */ React2__default.default.createElement(
366
+ "input",
367
+ {
368
+ type: "text",
369
+ value: playlistName,
370
+ onChange: (e) => setPlaylistName(e.target.value),
371
+ placeholder: "\u8F93\u5165\u64AD\u653E\u5217\u8868\u540D\u79F0...",
372
+ className: "w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
373
+ }
374
+ )), /* @__PURE__ */ React2__default.default.createElement("div", null, /* @__PURE__ */ React2__default.default.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u63CF\u8FF0"), /* @__PURE__ */ React2__default.default.createElement(
375
+ "textarea",
376
+ {
377
+ value: playlistDescription,
378
+ onChange: (e) => setPlaylistDescription(e.target.value),
379
+ placeholder: "\u8F93\u5165\u63CF\u8FF0\u4FE1\u606F...",
380
+ rows: 3,
381
+ className: "w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
382
+ }
383
+ )), /* @__PURE__ */ React2__default.default.createElement("div", { className: "flex items-center gap-6" }, /* @__PURE__ */ React2__default.default.createElement("label", { className: "flex items-center gap-2 cursor-pointer" }, /* @__PURE__ */ React2__default.default.createElement(
384
+ "input",
385
+ {
386
+ type: "checkbox",
387
+ checked: loop,
388
+ onChange: (e) => setLoop(e.target.checked),
389
+ className: "w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
390
+ }
391
+ ), /* @__PURE__ */ React2__default.default.createElement("span", { className: "text-sm text-gray-700 dark:text-gray-300" }, "\u5217\u8868\u5FAA\u73AF")), /* @__PURE__ */ React2__default.default.createElement("label", { className: "flex items-center gap-2 cursor-pointer" }, /* @__PURE__ */ React2__default.default.createElement(
392
+ "input",
393
+ {
394
+ type: "checkbox",
395
+ checked: autoPlay,
396
+ onChange: (e) => setAutoPlay(e.target.checked),
397
+ className: "w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
398
+ }
399
+ ), /* @__PURE__ */ React2__default.default.createElement("span", { className: "text-sm text-gray-700 dark:text-gray-300" }, "\u81EA\u52A8\u64AD\u653E"))), /* @__PURE__ */ React2__default.default.createElement(
400
+ "button",
401
+ {
402
+ onClick: () => setShowAdvanced(!showAdvanced),
403
+ className: "flex items-center gap-2 text-sm text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300"
404
+ },
405
+ /* @__PURE__ */ React2__default.default.createElement(lucideReact.Settings, { className: "w-4 h-4" }),
406
+ "\u9AD8\u7EA7\u9009\u9879",
407
+ showAdvanced ? /* @__PURE__ */ React2__default.default.createElement(lucideReact.ChevronUp, { className: "w-4 h-4" }) : /* @__PURE__ */ React2__default.default.createElement(lucideReact.ChevronDown, { className: "w-4 h-4" })
408
+ ), showAdvanced && /* @__PURE__ */ React2__default.default.createElement("div", { className: "space-y-4 pt-4 border-t border-gray-200 dark:border-gray-700" }, /* @__PURE__ */ React2__default.default.createElement("div", null, /* @__PURE__ */ React2__default.default.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u9884\u52A0\u8F7D\u7B56\u7565"), /* @__PURE__ */ React2__default.default.createElement(
409
+ "select",
410
+ {
411
+ value: preloadStrategy,
412
+ onChange: (e) => setPreloadStrategy(e.target.value),
413
+ className: "w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
414
+ },
415
+ /* @__PURE__ */ React2__default.default.createElement("option", { value: "none" }, "\u4E0D\u9884\u52A0\u8F7D"),
416
+ /* @__PURE__ */ React2__default.default.createElement("option", { value: "next" }, "\u9884\u52A0\u8F7D\u4E0B\u4E00\u4E2A"),
417
+ /* @__PURE__ */ React2__default.default.createElement("option", { value: "all" }, "\u9884\u52A0\u8F7D\u5168\u90E8")
418
+ )))), /* @__PURE__ */ React2__default.default.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React2__default.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React2__default.default.createElement("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white" }, "\u64AD\u653E\u8282\u70B9 (", nodes.length, ")"), /* @__PURE__ */ React2__default.default.createElement(
419
+ "button",
420
+ {
421
+ onClick: addNode,
422
+ className: "px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors flex items-center gap-2"
423
+ },
424
+ /* @__PURE__ */ React2__default.default.createElement(lucideReact.Plus, { className: "w-4 h-4" }),
425
+ "\u6DFB\u52A0\u8282\u70B9"
426
+ )), nodes.length === 0 ? /* @__PURE__ */ React2__default.default.createElement("div", { className: "p-8 text-center text-gray-500 border-2 border-dashed border-gray-300 dark:border-gray-700 rounded-lg" }, '\u6682\u65E0\u8282\u70B9\uFF0C\u8BF7\u70B9\u51FB"\u6DFB\u52A0\u8282\u70B9"\u6309\u94AE\u5F00\u59CB') : nodes.map((node, index2) => /* @__PURE__ */ React2__default.default.createElement(
427
+ "div",
428
+ {
429
+ key: index2,
430
+ className: "border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden"
431
+ },
432
+ /* @__PURE__ */ React2__default.default.createElement("div", { className: "flex items-center gap-3 p-4 bg-gray-50 dark:bg-gray-800" }, /* @__PURE__ */ React2__default.default.createElement(
433
+ "button",
434
+ {
435
+ className: "cursor-move text-gray-400 hover:text-gray-600 dark:hover:text-gray-300",
436
+ title: "\u62D6\u62FD\u6392\u5E8F"
437
+ },
438
+ /* @__PURE__ */ React2__default.default.createElement(lucideReact.GripVertical, { className: "w-5 h-5" })
439
+ ), /* @__PURE__ */ React2__default.default.createElement(
440
+ "button",
441
+ {
442
+ onClick: () => toggleNode(index2),
443
+ className: "flex-1 flex items-center justify-between text-left"
444
+ },
445
+ /* @__PURE__ */ React2__default.default.createElement("span", { className: "font-medium text-gray-900 dark:text-white" }, index2 + 1, ". ", node.name || "\u672A\u547D\u540D\u8282\u70B9"),
446
+ expandedNodes.has(index2) ? /* @__PURE__ */ React2__default.default.createElement(lucideReact.ChevronUp, { className: "w-5 h-5 text-gray-400" }) : /* @__PURE__ */ React2__default.default.createElement(lucideReact.ChevronDown, { className: "w-5 h-5 text-gray-400" })
447
+ ), /* @__PURE__ */ React2__default.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React2__default.default.createElement(
448
+ "button",
449
+ {
450
+ onClick: () => moveNode(index2, "up"),
451
+ disabled: index2 === 0,
452
+ className: "p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 disabled:opacity-30",
453
+ title: "\u4E0A\u79FB"
454
+ },
455
+ /* @__PURE__ */ React2__default.default.createElement(lucideReact.ChevronUp, { className: "w-5 h-5" })
456
+ ), /* @__PURE__ */ React2__default.default.createElement(
457
+ "button",
458
+ {
459
+ onClick: () => moveNode(index2, "down"),
460
+ disabled: index2 === nodes.length - 1,
461
+ className: "p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 disabled:opacity-30",
462
+ title: "\u4E0B\u79FB"
463
+ },
464
+ /* @__PURE__ */ React2__default.default.createElement(lucideReact.ChevronDown, { className: "w-5 h-5" })
465
+ ), /* @__PURE__ */ React2__default.default.createElement(
466
+ "button",
467
+ {
468
+ onClick: () => removeNode(index2),
469
+ className: "p-1 text-red-400 hover:text-red-600 dark:hover:text-red-300",
470
+ title: "\u5220\u9664"
471
+ },
472
+ /* @__PURE__ */ React2__default.default.createElement(lucideReact.Trash2, { className: "w-5 h-5" })
473
+ ))),
474
+ expandedNodes.has(index2) && /* @__PURE__ */ React2__default.default.createElement("div", { className: "p-6 space-y-6 bg-white dark:bg-gray-900" }, /* @__PURE__ */ React2__default.default.createElement("div", { className: "grid grid-cols-2 gap-4" }, /* @__PURE__ */ React2__default.default.createElement("div", null, /* @__PURE__ */ React2__default.default.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u8282\u70B9\u540D\u79F0 ", /* @__PURE__ */ React2__default.default.createElement("span", { className: "text-red-500" }, "*")), /* @__PURE__ */ React2__default.default.createElement(
475
+ "input",
476
+ {
477
+ type: "text",
478
+ value: node.name,
479
+ onChange: (e) => updateNode(index2, { name: e.target.value }),
480
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
481
+ }
482
+ )), /* @__PURE__ */ React2__default.default.createElement("div", null, /* @__PURE__ */ React2__default.default.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u65F6\u957F\uFF08\u79D2\uFF09"), /* @__PURE__ */ React2__default.default.createElement(
483
+ "input",
484
+ {
485
+ type: "number",
486
+ value: node.duration || "",
487
+ onChange: (e) => updateNode(index2, { duration: parseInt(e.target.value) || void 0 }),
488
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
489
+ }
490
+ ))), /* @__PURE__ */ React2__default.default.createElement("div", null, /* @__PURE__ */ React2__default.default.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u63CF\u8FF0"), /* @__PURE__ */ React2__default.default.createElement(
491
+ "textarea",
492
+ {
493
+ value: node.description || "",
494
+ onChange: (e) => updateNode(index2, { description: e.target.value }),
495
+ rows: 2,
496
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
497
+ }
498
+ )), /* @__PURE__ */ React2__default.default.createElement("label", { className: "flex items-center gap-2 cursor-pointer" }, /* @__PURE__ */ React2__default.default.createElement(
499
+ "input",
500
+ {
501
+ type: "checkbox",
502
+ checked: node.loop,
503
+ onChange: (e) => updateNode(index2, { loop: e.target.checked }),
504
+ className: "w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
505
+ }
506
+ ), /* @__PURE__ */ React2__default.default.createElement("span", { className: "text-sm text-gray-700 dark:text-gray-300" }, "\u5355\u66F2\u5FAA\u73AF")), /* @__PURE__ */ React2__default.default.createElement("div", { className: "grid grid-cols-1 gap-6 pt-4 border-t border-gray-200 dark:border-gray-700" }, /* @__PURE__ */ React2__default.default.createElement(
507
+ MmdResourceSelector,
508
+ {
509
+ resourceType: "model",
510
+ fileService,
511
+ userId,
512
+ value: node.modelFileId,
513
+ onChange: (fileId) => updateNode(index2, { modelFileId: fileId }),
514
+ required: true
515
+ }
516
+ ), /* @__PURE__ */ React2__default.default.createElement(
517
+ MmdResourceSelector,
518
+ {
519
+ resourceType: "motion",
520
+ fileService,
521
+ userId,
522
+ value: node.motionFileId,
523
+ onChange: (fileId) => updateNode(index2, { motionFileId: fileId })
524
+ }
525
+ ), /* @__PURE__ */ React2__default.default.createElement(
526
+ MmdResourceSelector,
527
+ {
528
+ resourceType: "audio",
529
+ fileService,
530
+ userId,
531
+ value: node.audioFileId,
532
+ onChange: (fileId) => updateNode(index2, { audioFileId: fileId })
533
+ }
534
+ )))
535
+ ))));
536
+ };
537
+
538
+ // src/business/mmd/admin/components/MmdAdminPanel.tsx
539
+ var MmdAdminPanel = ({
540
+ fileService,
541
+ userId,
542
+ apiBaseUrl = "/api/mmd",
543
+ showAdvancedOptions = true,
544
+ className = ""
545
+ }) => {
546
+ const [activeTab, setActiveTab] = React2.useState("playlists");
547
+ const [showEditor, setShowEditor] = React2.useState(false);
548
+ const [editingPlaylistId, setEditingPlaylistId] = React2.useState();
549
+ const tabs = [
550
+ { id: "playlists", label: "\u64AD\u653E\u5217\u8868", icon: lucideReact.List },
551
+ { id: "presets", label: "\u9884\u8BBE\u9879", icon: lucideReact.Database },
552
+ { id: "resources", label: "\u8D44\u6E90\u7BA1\u7406", icon: lucideReact.Settings },
553
+ ...showAdvancedOptions ? [{ id: "stats", label: "\u7EDF\u8BA1", icon: lucideReact.BarChart3 }] : []
554
+ ];
555
+ const handleCreatePlaylist = () => {
556
+ setEditingPlaylistId(void 0);
557
+ setShowEditor(true);
558
+ };
559
+ const handleCloseEditor = () => {
560
+ setShowEditor(false);
561
+ setEditingPlaylistId(void 0);
562
+ };
563
+ const handleSaveSuccess = (playlist) => {
564
+ console.log("\u4FDD\u5B58\u6210\u529F:", playlist);
565
+ handleCloseEditor();
566
+ };
567
+ if (showEditor) {
568
+ return /* @__PURE__ */ React2__default.default.createElement(
569
+ MmdPlaylistEditor,
570
+ {
571
+ playlistId: editingPlaylistId,
572
+ fileService,
573
+ userId,
574
+ onSave: handleSaveSuccess,
575
+ onCancel: handleCloseEditor
576
+ }
577
+ );
578
+ }
579
+ return /* @__PURE__ */ React2__default.default.createElement("div", { className: clsx.clsx("min-h-screen bg-gray-50 dark:bg-gray-900", className) }, /* @__PURE__ */ React2__default.default.createElement("div", { className: "bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700" }, /* @__PURE__ */ React2__default.default.createElement("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" }, /* @__PURE__ */ React2__default.default.createElement("div", { className: "flex items-center justify-between h-16" }, /* @__PURE__ */ React2__default.default.createElement("h1", { className: "text-2xl font-bold text-gray-900 dark:text-white" }, "MMD \u540E\u53F0\u7BA1\u7406"), activeTab === "playlists" && /* @__PURE__ */ React2__default.default.createElement(
580
+ "button",
581
+ {
582
+ onClick: handleCreatePlaylist,
583
+ className: "px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center gap-2"
584
+ },
585
+ /* @__PURE__ */ React2__default.default.createElement(lucideReact.Plus, { className: "w-4 h-4" }),
586
+ "\u521B\u5EFA\u64AD\u653E\u5217\u8868"
587
+ )), /* @__PURE__ */ React2__default.default.createElement("div", { className: "flex space-x-8" }, tabs.map((tab) => {
588
+ const Icon = tab.icon;
589
+ return /* @__PURE__ */ React2__default.default.createElement(
590
+ "button",
591
+ {
592
+ key: tab.id,
593
+ onClick: () => setActiveTab(tab.id),
594
+ className: clsx.clsx("flex items-center gap-2 px-1 py-4 border-b-2 font-medium text-sm transition-colors", activeTab === tab.id ? "border-blue-600 text-blue-600 dark:text-blue-400" : "border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300")
595
+ },
596
+ /* @__PURE__ */ React2__default.default.createElement(Icon, { className: "w-4 h-4" }),
597
+ tab.label
598
+ );
599
+ })))), /* @__PURE__ */ React2__default.default.createElement("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8" }, activeTab === "playlists" && /* @__PURE__ */ React2__default.default.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React2__default.default.createElement("div", { className: "text-center py-12 text-gray-500" }, /* @__PURE__ */ React2__default.default.createElement(lucideReact.List, { className: "w-12 h-12 mx-auto mb-4 opacity-50" }), /* @__PURE__ */ React2__default.default.createElement("p", null, "\u64AD\u653E\u5217\u8868\u7BA1\u7406\u529F\u80FD\u5F00\u53D1\u4E2D..."), /* @__PURE__ */ React2__default.default.createElement("p", { className: "text-sm mt-2" }, '\u70B9\u51FB\u53F3\u4E0A\u89D2"\u521B\u5EFA\u64AD\u653E\u5217\u8868"\u6309\u94AE\u5F00\u59CB'))), activeTab === "presets" && /* @__PURE__ */ React2__default.default.createElement("div", { className: "text-center py-12 text-gray-500" }, /* @__PURE__ */ React2__default.default.createElement(lucideReact.Database, { className: "w-12 h-12 mx-auto mb-4 opacity-50" }), /* @__PURE__ */ React2__default.default.createElement("p", null, "\u9884\u8BBE\u9879\u7BA1\u7406\u529F\u80FD\u5F00\u53D1\u4E2D...")), activeTab === "resources" && /* @__PURE__ */ React2__default.default.createElement("div", { className: "text-center py-12 text-gray-500" }, /* @__PURE__ */ React2__default.default.createElement(lucideReact.Settings, { className: "w-12 h-12 mx-auto mb-4 opacity-50" }), /* @__PURE__ */ React2__default.default.createElement("p", null, "\u8D44\u6E90\u7BA1\u7406\u529F\u80FD\u5F00\u53D1\u4E2D...")), activeTab === "stats" && /* @__PURE__ */ React2__default.default.createElement("div", { className: "text-center py-12 text-gray-500" }, /* @__PURE__ */ React2__default.default.createElement(lucideReact.BarChart3, { className: "w-12 h-12 mx-auto mb-4 opacity-50" }), /* @__PURE__ */ React2__default.default.createElement("p", null, "\u7EDF\u8BA1\u529F\u80FD\u5F00\u53D1\u4E2D..."))));
600
+ };
601
+
602
+ // src/business/mmd/admin/utils.ts
603
+ function extractFileIdsFromPlaylist(playlist, nodes) {
604
+ const fileIds = /* @__PURE__ */ new Set();
605
+ if (playlist.thumbnailFileId) {
606
+ fileIds.add(playlist.thumbnailFileId);
607
+ }
608
+ for (const node of nodes) {
609
+ if (node.thumbnailFileId) fileIds.add(node.thumbnailFileId);
610
+ if (node.modelFileId) fileIds.add(node.modelFileId);
611
+ if (node.motionFileId) fileIds.add(node.motionFileId);
612
+ if (node.cameraFileId) fileIds.add(node.cameraFileId);
613
+ if (node.audioFileId) fileIds.add(node.audioFileId);
614
+ if (node.stageModelFileId) fileIds.add(node.stageModelFileId);
615
+ if (node.additionalMotionFileIds) {
616
+ node.additionalMotionFileIds.forEach((id) => fileIds.add(id));
617
+ }
618
+ }
619
+ return Array.from(fileIds);
620
+ }
621
+ function extractFileIdsFromResourceOptions(options) {
622
+ const fileIds = /* @__PURE__ */ new Set();
623
+ for (const option of options) {
624
+ fileIds.add(option.fileId);
625
+ if (option.thumbnailFileId) {
626
+ fileIds.add(option.thumbnailFileId);
627
+ }
628
+ }
629
+ return Array.from(fileIds);
630
+ }
631
+ function extractFileIdsFromPresetItem(item) {
632
+ const fileIds = /* @__PURE__ */ new Set();
633
+ if (item.thumbnailFileId) fileIds.add(item.thumbnailFileId);
634
+ if (item.modelFileId) fileIds.add(item.modelFileId);
635
+ if (item.motionFileId) fileIds.add(item.motionFileId);
636
+ if (item.cameraFileId) fileIds.add(item.cameraFileId);
637
+ if (item.audioFileId) fileIds.add(item.audioFileId);
638
+ if (item.stageModelFileId) fileIds.add(item.stageModelFileId);
639
+ if (item.additionalMotionFileIds) {
640
+ item.additionalMotionFileIds.forEach((id) => fileIds.add(id));
641
+ }
642
+ return Array.from(fileIds);
643
+ }
644
+ function convertPlaylistNodeToFrontend(node, fileUrls) {
645
+ return {
646
+ id: node.id,
647
+ playlistId: node.playlistId,
648
+ name: node.name,
649
+ description: node.description,
650
+ loop: node.loop,
651
+ duration: node.duration,
652
+ sortOrder: node.sortOrder,
653
+ config: node.config,
654
+ createdAt: node.createdAt,
655
+ updatedAt: node.updatedAt,
656
+ // URL 映射
657
+ thumbnailUrl: node.thumbnailFileId ? fileUrls[node.thumbnailFileId] : void 0,
658
+ modelUrl: fileUrls[node.modelFileId] || "",
659
+ motionUrl: node.motionFileId ? fileUrls[node.motionFileId] : void 0,
660
+ cameraUrl: node.cameraFileId ? fileUrls[node.cameraFileId] : void 0,
661
+ audioUrl: node.audioFileId ? fileUrls[node.audioFileId] : void 0,
662
+ stageModelUrl: node.stageModelFileId ? fileUrls[node.stageModelFileId] : void 0,
663
+ additionalMotionUrls: node.additionalMotionFileIds?.map((id) => fileUrls[id]).filter((url) => Boolean(url))
664
+ };
665
+ }
666
+ function convertPlaylistToFrontend(playlist, nodes, fileUrls) {
667
+ return {
668
+ id: playlist.id,
669
+ name: playlist.name,
670
+ description: playlist.description,
671
+ loop: playlist.loop,
672
+ preloadStrategy: playlist.preloadStrategy,
673
+ autoPlay: playlist.autoPlay,
674
+ status: playlist.status,
675
+ sortOrder: playlist.sortOrder,
676
+ config: playlist.config,
677
+ createdBy: playlist.createdBy,
678
+ createdAt: playlist.createdAt,
679
+ updatedAt: playlist.updatedAt,
680
+ deletedAt: playlist.deletedAt,
681
+ // URL 映射
682
+ thumbnailUrl: playlist.thumbnailFileId ? fileUrls[playlist.thumbnailFileId] : void 0,
683
+ // 转换节点
684
+ nodes: nodes.sort((a, b) => a.sortOrder - b.sortOrder).map((node) => convertPlaylistNodeToFrontend(node, fileUrls))
685
+ };
686
+ }
687
+ function convertResourceOptionToFrontend(option, fileUrls) {
688
+ return {
689
+ id: option.id,
690
+ name: option.name,
691
+ description: option.description,
692
+ resourceType: option.resourceType,
693
+ tags: option.tags,
694
+ sortOrder: option.sortOrder,
695
+ isActive: option.isActive,
696
+ createdBy: option.createdBy,
697
+ createdAt: option.createdAt,
698
+ updatedAt: option.updatedAt,
699
+ // URL 映射
700
+ fileUrl: fileUrls[option.fileId] || "",
701
+ thumbnailUrl: option.thumbnailFileId ? fileUrls[option.thumbnailFileId] : void 0
702
+ };
703
+ }
704
+ function convertPresetItemToFrontend(item, fileUrls) {
705
+ return {
706
+ id: item.id,
707
+ name: item.name,
708
+ description: item.description,
709
+ sortOrder: item.sortOrder,
710
+ isActive: item.isActive,
711
+ tags: item.tags,
712
+ createdBy: item.createdBy,
713
+ createdAt: item.createdAt,
714
+ updatedAt: item.updatedAt,
715
+ // URL 映射
716
+ thumbnailUrl: item.thumbnailFileId ? fileUrls[item.thumbnailFileId] : void 0,
717
+ modelUrl: fileUrls[item.modelFileId] || "",
718
+ motionUrl: item.motionFileId ? fileUrls[item.motionFileId] : void 0,
719
+ cameraUrl: item.cameraFileId ? fileUrls[item.cameraFileId] : void 0,
720
+ audioUrl: item.audioFileId ? fileUrls[item.audioFileId] : void 0,
721
+ stageModelUrl: item.stageModelFileId ? fileUrls[item.stageModelFileId] : void 0,
722
+ additionalMotionUrls: item.additionalMotionFileIds?.map((id) => fileUrls[id]).filter((url) => Boolean(url))
723
+ };
724
+ }
725
+ function convertNodeToMmdFormat(node) {
726
+ return {
727
+ id: node.id,
728
+ name: node.name,
729
+ loop: node.loop,
730
+ duration: node.duration,
731
+ thumbnail: node.thumbnailUrl,
732
+ resources: {
733
+ modelPath: node.modelUrl,
734
+ motionPath: node.motionUrl,
735
+ cameraPath: node.cameraUrl,
736
+ audioPath: node.audioUrl,
737
+ stageModelPath: node.stageModelUrl,
738
+ additionalMotions: node.additionalMotionUrls
739
+ }
740
+ };
741
+ }
742
+ function convertPlaylistToMmdConfig(playlist) {
743
+ return {
744
+ id: playlist.id,
745
+ name: playlist.name,
746
+ nodes: playlist.nodes.map(convertNodeToMmdFormat),
747
+ loop: playlist.loop,
748
+ preload: playlist.preloadStrategy,
749
+ autoPlay: playlist.autoPlay
750
+ };
751
+ }
752
+ function convertPresetItemToMmdResource(item) {
753
+ return {
754
+ id: item.id,
755
+ name: item.name,
756
+ thumbnail: item.thumbnailUrl,
757
+ description: item.description,
758
+ resources: {
759
+ modelPath: item.modelUrl,
760
+ motionPath: item.motionUrl,
761
+ cameraPath: item.cameraUrl,
762
+ audioPath: item.audioUrl,
763
+ stageModelPath: item.stageModelUrl,
764
+ additionalMotions: item.additionalMotionUrls
765
+ }
766
+ };
767
+ }
768
+ function convertResourceOptionsToMmdFormat(options) {
769
+ const grouped = {
770
+ models: [],
771
+ motions: [],
772
+ cameras: [],
773
+ audios: [],
774
+ stages: []
775
+ };
776
+ for (const option of options) {
777
+ const resourceOption = {
778
+ id: option.id,
779
+ name: option.name,
780
+ path: option.fileUrl,
781
+ thumbnail: option.thumbnailUrl
782
+ };
783
+ switch (option.resourceType) {
784
+ case "model":
785
+ grouped.models.push(resourceOption);
786
+ break;
787
+ case "motion":
788
+ grouped.motions.push(resourceOption);
789
+ break;
790
+ case "camera":
791
+ grouped.cameras.push(resourceOption);
792
+ break;
793
+ case "audio":
794
+ grouped.audios.push(resourceOption);
795
+ break;
796
+ case "stage":
797
+ grouped.stages.push(resourceOption);
798
+ break;
799
+ }
800
+ }
801
+ return grouped;
802
+ }
803
+ function validateFileUrls(requiredFileIds, fileUrls) {
804
+ const missingIds = requiredFileIds.filter((id) => !fileUrls[id]);
805
+ return {
806
+ valid: missingIds.length === 0,
807
+ missingIds
808
+ };
809
+ }
810
+ function generateMockFileUrls(fileIds) {
811
+ const urls = {};
812
+ for (const id of fileIds) {
813
+ urls[id] = "/mock/files/" + id;
814
+ }
815
+ return urls;
816
+ }
817
+ function mergeFileUrlMaps(...maps) {
818
+ return Object.assign({}, ...maps);
819
+ }
820
+ function extractPathsFromMmdResources(resources) {
821
+ const paths = [resources.modelPath];
822
+ if (resources.motionPath) paths.push(resources.motionPath);
823
+ if (resources.cameraPath) paths.push(resources.cameraPath);
824
+ if (resources.audioPath) paths.push(resources.audioPath);
825
+ if (resources.stageModelPath) {
826
+ if (Array.isArray(resources.stageModelPath)) {
827
+ paths.push(...resources.stageModelPath);
828
+ } else {
829
+ paths.push(resources.stageModelPath);
830
+ }
831
+ }
832
+ if (resources.additionalMotions) paths.push(...resources.additionalMotions);
833
+ return paths;
834
+ }
835
+ var mmdPlaylists = pgCore.pgTable(
836
+ "mmd_playlists",
837
+ {
838
+ /** 主键ID */
839
+ id: pgCore.uuid("id").primaryKey().defaultRandom(),
840
+ /** 播放列表名称 */
841
+ name: pgCore.varchar("name", { length: 255 }).notNull(),
842
+ /** 播放列表描述 */
843
+ description: pgCore.text("description"),
844
+ /** 是否启用列表循环 */
845
+ loop: pgCore.boolean("loop").notNull().default(false),
846
+ /** 预加载策略: none, next, all */
847
+ preloadStrategy: pgCore.varchar("preload_strategy", { length: 20 }).notNull().default("none"),
848
+ /** 是否自动播放 */
849
+ autoPlay: pgCore.boolean("auto_play").notNull().default(false),
850
+ /** 播放列表缩略图文件ID (关联 file_metadata.id) */
851
+ thumbnailFileId: pgCore.uuid("thumbnail_file_id"),
852
+ /** 播放列表状态: draft, published, archived */
853
+ status: pgCore.varchar("status", { length: 20 }).notNull().default("draft"),
854
+ /** 显示顺序 */
855
+ sortOrder: pgCore.integer("sort_order").notNull().default(0),
856
+ /** 额外配置(JSON格式,存储舞台配置等) */
857
+ config: pgCore.json("config"),
858
+ /** 创建者ID */
859
+ createdBy: pgCore.varchar("created_by", { length: 255 }).notNull(),
860
+ /** 创建时间 */
861
+ createdAt: pgCore.timestamp("created_at").defaultNow().notNull(),
862
+ /** 更新时间 */
863
+ updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull(),
864
+ /** 删除时间(软删除) */
865
+ deletedAt: pgCore.timestamp("deleted_at")
866
+ },
867
+ (table) => ({
868
+ /** 按状态查询的索引 */
869
+ statusIndex: pgCore.index("mmd_playlists_status_idx").on(table.status),
870
+ /** 按创建者查询的索引 */
871
+ createdByIndex: pgCore.index("mmd_playlists_created_by_idx").on(table.createdBy),
872
+ /** 按删除状态查询的索引 */
873
+ deletedAtIndex: pgCore.index("mmd_playlists_deleted_at_idx").on(table.deletedAt),
874
+ /** 按排序查询的索引 */
875
+ sortOrderIndex: pgCore.index("mmd_playlists_sort_order_idx").on(table.sortOrder)
876
+ })
877
+ );
878
+ var mmdPlaylistNodes = pgCore.pgTable(
879
+ "mmd_playlist_nodes",
880
+ {
881
+ /** 主键ID */
882
+ id: pgCore.uuid("id").primaryKey().defaultRandom(),
883
+ /** 所属播放列表ID */
884
+ playlistId: pgCore.uuid("playlist_id").references(() => mmdPlaylists.id, { onDelete: "cascade" }).notNull(),
885
+ /** 节点名称 */
886
+ name: pgCore.varchar("name", { length: 255 }).notNull(),
887
+ /** 节点描述 */
888
+ description: pgCore.text("description"),
889
+ /** 是否启用节点循环 */
890
+ loop: pgCore.boolean("loop").notNull().default(false),
891
+ /** 预计时长(秒) */
892
+ duration: pgCore.integer("duration"),
893
+ /** 节点缩略图文件ID */
894
+ thumbnailFileId: pgCore.uuid("thumbnail_file_id"),
895
+ /** 显示顺序 */
896
+ sortOrder: pgCore.integer("sort_order").notNull().default(0),
897
+ /** 模型文件ID (关联 file_metadata.id) */
898
+ modelFileId: pgCore.uuid("model_file_id").notNull(),
899
+ /** 动作文件ID */
900
+ motionFileId: pgCore.uuid("motion_file_id"),
901
+ /** 相机动画文件ID */
902
+ cameraFileId: pgCore.uuid("camera_file_id"),
903
+ /** 音频文件ID */
904
+ audioFileId: pgCore.uuid("audio_file_id"),
905
+ /** 舞台模型文件ID */
906
+ stageModelFileId: pgCore.uuid("stage_model_file_id"),
907
+ /** 附加动作文件ID列表(JSON数组) */
908
+ additionalMotionFileIds: pgCore.json("additional_motion_file_ids").$type(),
909
+ /** 额外配置(JSON格式) */
910
+ config: pgCore.json("config"),
911
+ /** 创建时间 */
912
+ createdAt: pgCore.timestamp("created_at").defaultNow().notNull(),
913
+ /** 更新时间 */
914
+ updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull()
915
+ },
916
+ (table) => ({
917
+ /** 按播放列表查询的索引 */
918
+ playlistIndex: pgCore.index("mmd_playlist_nodes_playlist_idx").on(table.playlistId),
919
+ /** 按排序查询的索引 */
920
+ sortOrderIndex: pgCore.index("mmd_playlist_nodes_sort_order_idx").on(table.sortOrder),
921
+ /** 按模型文件查询的索引 */
922
+ modelFileIndex: pgCore.index("mmd_playlist_nodes_model_file_idx").on(table.modelFileId),
923
+ /** 组合索引:播放列表+排序 */
924
+ playlistSortIndex: pgCore.index("mmd_playlist_nodes_playlist_sort_idx").on(
925
+ table.playlistId,
926
+ table.sortOrder
927
+ )
928
+ })
929
+ );
930
+ var mmdResourceOptions = pgCore.pgTable(
931
+ "mmd_resource_options",
932
+ {
933
+ /** 主键ID */
934
+ id: pgCore.uuid("id").primaryKey().defaultRandom(),
935
+ /** 资源名称 */
936
+ name: pgCore.varchar("name", { length: 255 }).notNull(),
937
+ /** 资源描述 */
938
+ description: pgCore.text("description"),
939
+ /** 资源类型: model, motion, camera, audio, stage */
940
+ resourceType: pgCore.varchar("resource_type", { length: 20 }).notNull(),
941
+ /** 文件ID (关联 file_metadata.id) */
942
+ fileId: pgCore.uuid("file_id").notNull(),
943
+ /** 缩略图文件ID */
944
+ thumbnailFileId: pgCore.uuid("thumbnail_file_id"),
945
+ /** 资源标签(JSON数组,用于分类和筛选) */
946
+ tags: pgCore.json("tags").$type(),
947
+ /** 显示顺序 */
948
+ sortOrder: pgCore.integer("sort_order").notNull().default(0),
949
+ /** 是否启用 */
950
+ isActive: pgCore.boolean("is_active").notNull().default(true),
951
+ /** 创建者ID */
952
+ createdBy: pgCore.varchar("created_by", { length: 255 }).notNull(),
953
+ /** 创建时间 */
954
+ createdAt: pgCore.timestamp("created_at").defaultNow().notNull(),
955
+ /** 更新时间 */
956
+ updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull()
957
+ },
958
+ (table) => ({
959
+ /** 按资源类型查询的索引 */
960
+ resourceTypeIndex: pgCore.index("mmd_resource_options_resource_type_idx").on(table.resourceType),
961
+ /** 按文件ID查询的索引 */
962
+ fileIdIndex: pgCore.index("mmd_resource_options_file_id_idx").on(table.fileId),
963
+ /** 按活跃状态查询的索引 */
964
+ isActiveIndex: pgCore.index("mmd_resource_options_is_active_idx").on(table.isActive),
965
+ /** 按创建者查询的索引 */
966
+ createdByIndex: pgCore.index("mmd_resource_options_created_by_idx").on(table.createdBy),
967
+ /** 组合索引:资源类型+活跃状态+排序 */
968
+ typeActiveSortIndex: pgCore.index("mmd_resource_options_type_active_sort_idx").on(
969
+ table.resourceType,
970
+ table.isActive,
971
+ table.sortOrder
972
+ )
973
+ })
974
+ );
975
+ var mmdPresetItems = pgCore.pgTable(
976
+ "mmd_preset_items",
977
+ {
978
+ /** 主键ID */
979
+ id: pgCore.uuid("id").primaryKey().defaultRandom(),
980
+ /** 预设名称 */
981
+ name: pgCore.varchar("name", { length: 255 }).notNull(),
982
+ /** 预设描述 */
983
+ description: pgCore.text("description"),
984
+ /** 缩略图文件ID */
985
+ thumbnailFileId: pgCore.uuid("thumbnail_file_id"),
986
+ /** 模型文件ID */
987
+ modelFileId: pgCore.uuid("model_file_id").notNull(),
988
+ /** 动作文件ID */
989
+ motionFileId: pgCore.uuid("motion_file_id"),
990
+ /** 相机动画文件ID */
991
+ cameraFileId: pgCore.uuid("camera_file_id"),
992
+ /** 音频文件ID */
993
+ audioFileId: pgCore.uuid("audio_file_id"),
994
+ /** 舞台模型文件ID */
995
+ stageModelFileId: pgCore.uuid("stage_model_file_id"),
996
+ /** 附加动作文件ID列表(JSON数组) */
997
+ additionalMotionFileIds: pgCore.json("additional_motion_file_ids").$type(),
998
+ /** 显示顺序 */
999
+ sortOrder: pgCore.integer("sort_order").notNull().default(0),
1000
+ /** 是否启用 */
1001
+ isActive: pgCore.boolean("is_active").notNull().default(true),
1002
+ /** 预设标签(JSON数组) */
1003
+ tags: pgCore.json("tags").$type(),
1004
+ /** 创建者ID */
1005
+ createdBy: pgCore.varchar("created_by", { length: 255 }).notNull(),
1006
+ /** 创建时间 */
1007
+ createdAt: pgCore.timestamp("created_at").defaultNow().notNull(),
1008
+ /** 更新时间 */
1009
+ updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull()
1010
+ },
1011
+ (table) => ({
1012
+ /** 按活跃状态查询的索引 */
1013
+ isActiveIndex: pgCore.index("mmd_preset_items_is_active_idx").on(table.isActive),
1014
+ /** 按排序查询的索引 */
1015
+ sortOrderIndex: pgCore.index("mmd_preset_items_sort_order_idx").on(table.sortOrder),
1016
+ /** 按创建者查询的索引 */
1017
+ createdByIndex: pgCore.index("mmd_preset_items_created_by_idx").on(table.createdBy),
1018
+ /** 按模型文件查询的索引 */
1019
+ modelFileIndex: pgCore.index("mmd_preset_items_model_file_idx").on(table.modelFileId)
1020
+ })
1021
+ );
1022
+ var mmdPlaylistsRelations = drizzleOrm.relations(mmdPlaylists, ({ many }) => ({
1023
+ nodes: many(mmdPlaylistNodes)
1024
+ }));
1025
+ var mmdPlaylistNodesRelations = drizzleOrm.relations(mmdPlaylistNodes, ({ one }) => ({
1026
+ playlist: one(mmdPlaylists, {
1027
+ fields: [mmdPlaylistNodes.playlistId],
1028
+ references: [mmdPlaylists.id]
1029
+ })
1030
+ }));
1031
+
1032
+ exports.MMD_RESOURCE_TYPE_CONFIGS = MMD_RESOURCE_TYPE_CONFIGS;
1033
+ exports.MmdAdminPanel = MmdAdminPanel;
1034
+ exports.MmdPlaylistEditor = MmdPlaylistEditor;
1035
+ exports.MmdResourceSelector = MmdResourceSelector;
1036
+ exports.convertNodeToMmdFormat = convertNodeToMmdFormat;
1037
+ exports.convertPlaylistNodeToFrontend = convertPlaylistNodeToFrontend;
1038
+ exports.convertPlaylistToFrontend = convertPlaylistToFrontend;
1039
+ exports.convertPlaylistToMmdConfig = convertPlaylistToMmdConfig;
1040
+ exports.convertPresetItemToFrontend = convertPresetItemToFrontend;
1041
+ exports.convertPresetItemToMmdResource = convertPresetItemToMmdResource;
1042
+ exports.convertResourceOptionToFrontend = convertResourceOptionToFrontend;
1043
+ exports.convertResourceOptionsToMmdFormat = convertResourceOptionsToMmdFormat;
1044
+ exports.extractFileIdsFromPlaylist = extractFileIdsFromPlaylist;
1045
+ exports.extractFileIdsFromPresetItem = extractFileIdsFromPresetItem;
1046
+ exports.extractFileIdsFromResourceOptions = extractFileIdsFromResourceOptions;
1047
+ exports.extractPathsFromMmdResources = extractPathsFromMmdResources;
1048
+ exports.generateMockFileUrls = generateMockFileUrls;
1049
+ exports.mergeFileUrlMaps = mergeFileUrlMaps;
1050
+ exports.mmdPlaylistNodes = mmdPlaylistNodes;
1051
+ exports.mmdPlaylistNodesRelations = mmdPlaylistNodesRelations;
1052
+ exports.mmdPlaylists = mmdPlaylists;
1053
+ exports.mmdPlaylistsRelations = mmdPlaylistsRelations;
1054
+ exports.mmdPresetItems = mmdPresetItems;
1055
+ exports.mmdResourceOptions = mmdResourceOptions;
1056
+ exports.validateFileUrls = validateFileUrls;
1057
+ //# sourceMappingURL=index.js.map
1058
+ //# sourceMappingURL=index.js.map