sa2kit 1.6.9 → 1.6.11

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 (149) hide show
  1. package/dist/ConfigService-BxK06xP6.d.mts +262 -0
  2. package/dist/ConfigService-BxK06xP6.d.ts +262 -0
  3. package/dist/UniversalFileService-BuHN-jrR.d.ts +515 -0
  4. package/dist/UniversalFileService-CGGzYeeF.d.mts +515 -0
  5. package/dist/analytics/index.d.mts +1084 -0
  6. package/dist/analytics/index.d.ts +1084 -0
  7. package/dist/analytics/server/index.d.mts +499 -0
  8. package/dist/analytics/server/index.d.ts +499 -0
  9. package/dist/api/index.d.mts +248 -0
  10. package/dist/api/index.d.ts +248 -0
  11. package/dist/audioDetection/index.d.mts +449 -0
  12. package/dist/audioDetection/index.d.ts +449 -0
  13. package/dist/auth/client/index.d.mts +32 -0
  14. package/dist/auth/client/index.d.ts +32 -0
  15. package/dist/auth/client/index.js +4 -4
  16. package/dist/auth/client/index.mjs +1 -1
  17. package/dist/auth/components/index.d.mts +227 -0
  18. package/dist/auth/components/index.d.ts +227 -0
  19. package/dist/auth/components/index.js +4 -4
  20. package/dist/auth/components/index.mjs +1 -1
  21. package/dist/auth/hooks/index.d.mts +31 -0
  22. package/dist/auth/hooks/index.d.ts +31 -0
  23. package/dist/auth/hooks/index.js +3 -3
  24. package/dist/auth/hooks/index.mjs +1 -1
  25. package/dist/auth/index.d.mts +41 -0
  26. package/dist/auth/index.d.ts +41 -0
  27. package/dist/auth/index.js +17 -36
  28. package/dist/auth/index.js.map +1 -1
  29. package/dist/auth/index.mjs +3 -33
  30. package/dist/auth/index.mjs.map +1 -1
  31. package/dist/auth/middleware/index.d.mts +75 -0
  32. package/dist/auth/middleware/index.d.ts +75 -0
  33. package/dist/auth/routes/index.d.mts +261 -0
  34. package/dist/auth/routes/index.d.ts +261 -0
  35. package/dist/auth/schema/index.d.mts +789 -0
  36. package/dist/auth/schema/index.d.ts +789 -0
  37. package/dist/auth/services/index.d.mts +48 -0
  38. package/dist/auth/services/index.d.ts +48 -0
  39. package/dist/base-api-client-B-yUCal3.d.ts +103 -0
  40. package/dist/base-api-client-BQ8ZPZjk.d.mts +103 -0
  41. package/dist/calendar/index.d.mts +1197 -0
  42. package/dist/calendar/index.d.ts +1197 -0
  43. package/dist/calendar/index.js +8 -8
  44. package/dist/calendar/index.js.map +1 -1
  45. package/dist/calendar/index.mjs +1 -1
  46. package/dist/calendar/index.mjs.map +1 -1
  47. package/dist/calendar/routes/index.d.mts +118 -0
  48. package/dist/calendar/routes/index.d.ts +118 -0
  49. package/dist/calendar/server.d.mts +1184 -0
  50. package/dist/calendar/server.d.ts +1184 -0
  51. package/dist/{chunk-ROEYW4A7.js → chunk-5QMBZP7S.js} +2 -2
  52. package/dist/chunk-5QMBZP7S.js.map +1 -0
  53. package/dist/chunk-6BZ3QFA5.mjs +33 -0
  54. package/dist/chunk-6BZ3QFA5.mjs.map +1 -0
  55. package/dist/{chunk-HEMA7SWK.mjs → chunk-6YKMCPQI.mjs} +2 -2
  56. package/dist/chunk-6YKMCPQI.mjs.map +1 -0
  57. package/dist/chunk-BH5TLVS5.mjs +1593 -0
  58. package/dist/chunk-BH5TLVS5.mjs.map +1 -0
  59. package/dist/chunk-E72D5KHY.js +1723 -0
  60. package/dist/chunk-E72D5KHY.js.map +1 -0
  61. package/dist/chunk-FAHLZIYQ.js +36 -0
  62. package/dist/chunk-FAHLZIYQ.js.map +1 -0
  63. package/dist/{chunk-KGRQNEIR.mjs → chunk-KW5JH6V6.mjs} +2 -2
  64. package/dist/chunk-KW5JH6V6.mjs.map +1 -0
  65. package/dist/{chunk-O26VCNS3.js → chunk-UOFTHYIH.js} +2 -2
  66. package/dist/chunk-UOFTHYIH.js.map +1 -0
  67. package/dist/config/index.d.mts +64 -0
  68. package/dist/config/index.d.ts +64 -0
  69. package/dist/config/server/index.d.mts +1533 -0
  70. package/dist/config/server/index.d.ts +1533 -0
  71. package/dist/drizzle-auth-service-Bxlovhv8.d.ts +145 -0
  72. package/dist/drizzle-auth-service-DZY2F1sv.d.mts +145 -0
  73. package/dist/drizzle-schema-BNhqj2AZ.d.mts +1114 -0
  74. package/dist/drizzle-schema-BNhqj2AZ.d.ts +1114 -0
  75. package/dist/enums-Dume-V5Y.d.mts +16 -0
  76. package/dist/enums-Dume-V5Y.d.ts +16 -0
  77. package/dist/i18n/index.d.mts +417 -0
  78. package/dist/i18n/index.d.ts +417 -0
  79. package/dist/imageCrop/index.d.mts +165 -0
  80. package/dist/imageCrop/index.d.ts +165 -0
  81. package/dist/index-DSel44Ke.d.mts +93 -0
  82. package/dist/index-DSel44Ke.d.ts +93 -0
  83. package/dist/index.d.mts +933 -0
  84. package/dist/index.d.ts +933 -0
  85. package/dist/index.js +1626 -2184
  86. package/dist/index.js.map +1 -1
  87. package/dist/index.mjs +708 -1543
  88. package/dist/index.mjs.map +1 -1
  89. package/dist/logger/index.d.mts +125 -0
  90. package/dist/logger/index.d.ts +125 -0
  91. package/dist/mmd/admin/index.d.mts +487 -0
  92. package/dist/mmd/admin/index.d.ts +487 -0
  93. package/dist/mmd/index.d.mts +1412 -0
  94. package/dist/mmd/index.d.ts +1412 -0
  95. package/dist/mmd/index.js +2695 -625
  96. package/dist/mmd/index.js.map +1 -1
  97. package/dist/mmd/index.mjs +2501 -448
  98. package/dist/mmd/index.mjs.map +1 -1
  99. package/dist/mmd/server/index.d.mts +138 -0
  100. package/dist/mmd/server/index.d.ts +138 -0
  101. package/dist/music/index.d.mts +74 -0
  102. package/dist/music/index.d.ts +74 -0
  103. package/dist/music/server/index.d.mts +1 -0
  104. package/dist/music/server/index.d.ts +1 -0
  105. package/dist/request/index.d.mts +51 -0
  106. package/dist/request/index.d.ts +51 -0
  107. package/dist/storage/index.d.mts +75 -0
  108. package/dist/storage/index.d.ts +75 -0
  109. package/dist/testYourself/admin/index.d.mts +58 -0
  110. package/dist/testYourself/admin/index.d.ts +58 -0
  111. package/dist/testYourself/index.d.mts +53 -0
  112. package/dist/testYourself/index.d.ts +53 -0
  113. package/dist/testYourself/server/index.d.mts +1029 -0
  114. package/dist/testYourself/server/index.d.ts +1029 -0
  115. package/dist/types-BB-7_WtE.d.mts +253 -0
  116. package/dist/types-BB-7_WtE.d.ts +253 -0
  117. package/dist/types-BINlP9MK.d.mts +286 -0
  118. package/dist/types-BINlP9MK.d.ts +286 -0
  119. package/dist/types-BaZccpvk.d.mts +48 -0
  120. package/dist/types-BaZccpvk.d.ts +48 -0
  121. package/dist/types-CK4We_aI.d.mts +270 -0
  122. package/dist/types-CK4We_aI.d.ts +270 -0
  123. package/dist/types-CbTsi9CZ.d.mts +31 -0
  124. package/dist/types-CbTsi9CZ.d.ts +31 -0
  125. package/dist/types-CiYK5Klf.d.mts +99 -0
  126. package/dist/types-D3R6GzOw.d.mts +70 -0
  127. package/dist/types-Dlu52uDy.d.ts +99 -0
  128. package/dist/types-iFeyR443.d.ts +70 -0
  129. package/dist/universalExport/index.d.mts +235 -0
  130. package/dist/universalExport/index.d.ts +235 -0
  131. package/dist/universalExport/server/index.d.mts +1270 -0
  132. package/dist/universalExport/server/index.d.ts +1270 -0
  133. package/dist/universalFile/index.d.mts +480 -0
  134. package/dist/universalFile/index.d.ts +480 -0
  135. package/dist/universalFile/server/index.d.mts +4516 -0
  136. package/dist/universalFile/server/index.d.ts +4516 -0
  137. package/dist/useElectronStorage-Dj0rcorG.d.mts +65 -0
  138. package/dist/useElectronStorage-DwnNfIhl.d.ts +65 -0
  139. package/dist/utils/index.d.mts +192 -0
  140. package/dist/utils/index.d.ts +192 -0
  141. package/package.json +2 -1
  142. package/dist/chunk-4FOBBWXW.mjs +0 -179
  143. package/dist/chunk-4FOBBWXW.mjs.map +0 -1
  144. package/dist/chunk-G6WRJ2H2.js +0 -187
  145. package/dist/chunk-G6WRJ2H2.js.map +0 -1
  146. package/dist/chunk-HEMA7SWK.mjs.map +0 -1
  147. package/dist/chunk-KGRQNEIR.mjs.map +0 -1
  148. package/dist/chunk-O26VCNS3.js.map +0 -1
  149. package/dist/chunk-ROEYW4A7.js.map +0 -1
@@ -1,9 +1,9 @@
1
1
  import { useMusic } from '../chunk-CDK3DHKM.mjs';
2
2
  import '../chunk-ZCLAF3XN.mjs';
3
3
  import '../chunk-BJTO5JO5.mjs';
4
- import React6, { forwardRef, useRef, useEffect, useImperativeHandle, useState, useCallback } from 'react';
5
- import * as THREE from 'three';
6
- import { UnrealBloomPass, OutlineEffect, EffectComposer, RenderPass, OrbitControls, MMDLoader, MMDAnimationHelper } from 'three-stdlib';
4
+ import React21, { forwardRef, useRef, useEffect, useImperativeHandle, useState, useCallback } from 'react';
5
+ import * as THREE3 from 'three';
6
+ import { OutlineEffect, OrbitControls, MMDLoader, MMDAnimationHelper } from 'three-stdlib';
7
7
  import { AlertCircle, Camera, Settings, RefreshCw, CameraOff, X, Check, SkipBack, Pause, Play, SkipForward, Repeat, Repeat1, Shuffle, ListMusic, Music, Search, Loader2, Grid3x3, Minimize, Maximize, Video, User, Image as Image$1 } from 'lucide-react';
8
8
  import { createPortal } from 'react-dom';
9
9
 
@@ -48,19 +48,1561 @@ var loadAmmo = (path = "/libs/ammo.wasm.js") => {
48
48
  return ammoPromise;
49
49
  };
50
50
 
51
+ // src/mmd/fx/HLSLToGLSLConverter.ts
52
+ var HLSLToGLSLConverter = class {
53
+ constructor() {
54
+ this.warnings = [];
55
+ this.uniforms = /* @__PURE__ */ new Map();
56
+ this.attributes = /* @__PURE__ */ new Map();
57
+ this.varyings = /* @__PURE__ */ new Map();
58
+ /**
59
+ * HLSL类型到GLSL类型的映射
60
+ */
61
+ this.typeMap = {
62
+ // 标量类型
63
+ "float": "float",
64
+ "int": "int",
65
+ "bool": "bool",
66
+ "half": "float",
67
+ // GLSL没有half,使用float
68
+ // 向量类型
69
+ "float2": "vec2",
70
+ "float3": "vec3",
71
+ "float4": "vec4",
72
+ "int2": "ivec2",
73
+ "int3": "ivec3",
74
+ "int4": "ivec4",
75
+ "bool2": "bvec2",
76
+ "bool3": "bvec3",
77
+ "bool4": "bvec4",
78
+ "half2": "vec2",
79
+ "half3": "vec3",
80
+ "half4": "vec4",
81
+ // 矩阵类型
82
+ "float2x2": "mat2",
83
+ "float3x3": "mat3",
84
+ "float4x4": "mat4",
85
+ "matrix": "mat4",
86
+ // 纹理类型
87
+ "texture": "sampler2D",
88
+ "texture2D": "sampler2D",
89
+ "sampler": "sampler2D",
90
+ "sampler2D": "sampler2D",
91
+ "samplerCUBE": "samplerCube"
92
+ };
93
+ /**
94
+ * HLSL函数到GLSL函数的映射
95
+ */
96
+ this.functionMap = {
97
+ // 纹理采样
98
+ "tex2D": "texture2D",
99
+ "tex2Dlod": "texture2DLod",
100
+ "texCUBE": "textureCube",
101
+ // 数学函数
102
+ "mul": "matrixCompMult",
103
+ // 注意:mul可能需要特殊处理
104
+ "lerp": "mix",
105
+ "frac": "fract",
106
+ "saturate": "clamp01",
107
+ // 需要自定义函数
108
+ "ddx": "dFdx",
109
+ "ddy": "dFdy",
110
+ // 向量函数
111
+ "length": "length",
112
+ "normalize": "normalize",
113
+ "dot": "dot",
114
+ "cross": "cross",
115
+ "reflect": "reflect",
116
+ "refract": "refract",
117
+ // 数学运算
118
+ "pow": "pow",
119
+ "exp": "exp",
120
+ "log": "log",
121
+ "sqrt": "sqrt",
122
+ "abs": "abs",
123
+ "sin": "sin",
124
+ "cos": "cos",
125
+ "tan": "tan",
126
+ "asin": "asin",
127
+ "acos": "acos",
128
+ "atan": "atan",
129
+ "atan2": "atan",
130
+ "floor": "floor",
131
+ "ceil": "ceil",
132
+ "min": "min",
133
+ "max": "max",
134
+ "clamp": "clamp",
135
+ "step": "step",
136
+ "smoothstep": "smoothstep"
137
+ };
138
+ /**
139
+ * HLSL语义到GLSL的映射
140
+ */
141
+ this.semanticMap = {
142
+ // 顶点着色器输入
143
+ "POSITION": "position",
144
+ "POSITION0": "position",
145
+ "NORMAL": "normal",
146
+ "NORMAL0": "normal",
147
+ "TEXCOORD": "uv",
148
+ "TEXCOORD0": "uv",
149
+ "TEXCOORD1": "uv2",
150
+ "COLOR": "color",
151
+ "COLOR0": "color",
152
+ "TANGENT": "tangent",
153
+ "BINORMAL": "binormal",
154
+ // 顶点着色器输出 / 像素着色器输入
155
+ "SV_POSITION": "gl_Position",
156
+ // 像素着色器输出
157
+ "SV_TARGET": "gl_FragColor",
158
+ "SV_TARGET0": "gl_FragColor"
159
+ };
160
+ }
161
+ /**
162
+ * 转换HLSL shader代码为GLSL
163
+ */
164
+ convert(hlslCode, shaderType) {
165
+ this.warnings = [];
166
+ this.uniforms.clear();
167
+ this.attributes.clear();
168
+ this.varyings.clear();
169
+ let glslCode = hlslCode;
170
+ glslCode = this.preprocessCode(glslCode);
171
+ glslCode = this.convertTypes(glslCode);
172
+ glslCode = this.convertFunctions(glslCode);
173
+ glslCode = this.convertSemantics(glslCode, shaderType);
174
+ glslCode = this.convertIOStructs(glslCode, shaderType);
175
+ glslCode = this.addGLSLHeader(glslCode);
176
+ glslCode = this.fixSyntaxDifferences(glslCode);
177
+ return {
178
+ glslCode,
179
+ uniforms: this.uniforms,
180
+ attributes: this.attributes,
181
+ varyings: this.varyings,
182
+ warnings: this.warnings
183
+ };
184
+ }
185
+ /**
186
+ * 预处理代码
187
+ */
188
+ preprocessCode(code) {
189
+ code = code.replace(/:\s*register\([^)]+\)/g, "");
190
+ code = code.replace(/:\s*packoffset\([^)]+\)/g, "");
191
+ return code;
192
+ }
193
+ /**
194
+ * 转换类型声明
195
+ */
196
+ convertTypes(code) {
197
+ for (const [hlslType, glslType] of Object.entries(this.typeMap)) {
198
+ const regex = new RegExp(`\\b${hlslType}\\b`, "g");
199
+ code = code.replace(regex, glslType);
200
+ }
201
+ return code;
202
+ }
203
+ /**
204
+ * 转换函数调用
205
+ */
206
+ convertFunctions(code) {
207
+ for (const [hlslFunc, glslFunc] of Object.entries(this.functionMap)) {
208
+ const regex = new RegExp(`\\b${hlslFunc}\\b`, "g");
209
+ code = code.replace(regex, glslFunc);
210
+ }
211
+ code = this.convertMulFunction(code);
212
+ code = code.replace(/\bclamp01\s*\(/g, "clamp(");
213
+ code = code.replace(/saturate\s*\(([^)]+)\)/g, "clamp($1, 0.0, 1.0)");
214
+ return code;
215
+ }
216
+ /**
217
+ * 转换mul()函数
218
+ * HLSL: mul(matrix, vector) 或 mul(vector, matrix)
219
+ * GLSL: matrix * vector 或 vector * matrix
220
+ */
221
+ convertMulFunction(code) {
222
+ code = code.replace(/mul\s*\(\s*([^,]+)\s*,\s*([^)]+)\s*\)/g, "($1 * $2)");
223
+ this.warnings.push("mul() function converted to (*) operator - may need manual adjustment for matrix multiplication order");
224
+ return code;
225
+ }
226
+ /**
227
+ * 转换语义
228
+ */
229
+ convertSemantics(code, shaderType) {
230
+ for (const [hlslSemantic, glslSemantic] of Object.entries(this.semanticMap)) {
231
+ const regex = new RegExp(`\\b${hlslSemantic}\\b`, "g");
232
+ code = code.replace(regex, glslSemantic);
233
+ }
234
+ return code;
235
+ }
236
+ /**
237
+ * 转换输入/输出结构
238
+ */
239
+ convertIOStructs(code, shaderType) {
240
+ const inputStructRegex = /struct\s+(\w+)\s*{([^}]+)}/g;
241
+ let match;
242
+ while ((match = inputStructRegex.exec(code)) !== null) {
243
+ if (!match[1] || !match[2]) continue;
244
+ match[1];
245
+ const structBody = match[2];
246
+ const members = this.parseStructMembers(structBody);
247
+ if (shaderType === "vertex") {
248
+ members.forEach((member) => {
249
+ this.attributes.set(member.name, {
250
+ type: member.type,
251
+ glslType: this.typeMap[member.type] || member.type,
252
+ semantic: member.semantic || ""
253
+ });
254
+ });
255
+ }
256
+ }
257
+ return code;
258
+ }
259
+ /**
260
+ * 解析结构体成员
261
+ */
262
+ parseStructMembers(structBody) {
263
+ const members = [];
264
+ const lines = structBody.split(";");
265
+ lines.forEach((line) => {
266
+ const trimmed = line.trim();
267
+ if (!trimmed) return;
268
+ const match = trimmed.match(/(\w+)\s+(\w+)\s*(?::\s*(\w+))?/);
269
+ if (match && match[1] && match[2]) {
270
+ members.push({
271
+ type: match[1],
272
+ name: match[2],
273
+ semantic: match[3]
274
+ });
275
+ }
276
+ });
277
+ return members;
278
+ }
279
+ /**
280
+ * 添加GLSL头部和辅助函数
281
+ */
282
+ addGLSLHeader(code) {
283
+ const header = `
284
+ // Auto-converted from HLSL to GLSL
285
+ #ifdef GL_ES
286
+ precision highp float;
287
+ #endif
288
+
289
+ // Helper functions for HLSL compatibility
290
+ vec3 mul(mat3 m, vec3 v) { return m * v; }
291
+ vec4 mul(mat4 m, vec4 v) { return m * v; }
292
+ float saturate(float x) { return clamp(x, 0.0, 1.0); }
293
+ vec2 saturate(vec2 x) { return clamp(x, 0.0, 1.0); }
294
+ vec3 saturate(vec3 x) { return clamp(x, 0.0, 1.0); }
295
+ vec4 saturate(vec4 x) { return clamp(x, 0.0, 1.0); }
296
+
297
+ `;
298
+ return header + code;
299
+ }
300
+ /**
301
+ * 修复语法差异
302
+ */
303
+ fixSyntaxDifferences(code) {
304
+ return code;
305
+ }
306
+ /**
307
+ * 从FX效果中提取并转换shader
308
+ */
309
+ convertFromFXEffect(effect, vertexShaderName, fragmentShaderName) {
310
+ const vsFunc = effect.shaderFunctions?.find((f) => f.name === vertexShaderName);
311
+ const fsFunc = effect.shaderFunctions?.find((f) => f.name === fragmentShaderName);
312
+ if (!vsFunc || !fsFunc) {
313
+ this.warnings.push(`Shader functions not found: ${vertexShaderName} or ${fragmentShaderName}`);
314
+ return null;
315
+ }
316
+ const vertexShader = this.convert(vsFunc.body, "vertex");
317
+ const fragmentShader = this.convert(fsFunc.body, "fragment");
318
+ return {
319
+ vertexShader,
320
+ fragmentShader
321
+ };
322
+ }
323
+ };
324
+
325
+ // src/mmd/fx/FXParser.ts
326
+ var FXParser = class {
327
+ constructor(options = {}) {
328
+ this.options = {
329
+ keepComments: options.keepComments ?? true,
330
+ parseTechniques: options.parseTechniques ?? true,
331
+ parseShaderFunctions: options.parseShaderFunctions ?? true,
332
+ parseTextures: options.parseTextures ?? true,
333
+ parseControllers: options.parseControllers ?? true,
334
+ convertToGLSL: options.convertToGLSL ?? false,
335
+ vertexShaderFunction: options.vertexShaderFunction,
336
+ fragmentShaderFunction: options.fragmentShaderFunction
337
+ };
338
+ this.hlslConverter = new HLSLToGLSLConverter();
339
+ }
340
+ /**
341
+ * 从URL加载并解析FX文件
342
+ */
343
+ async loadAndParse(url) {
344
+ const response = await fetch(url);
345
+ const content = await response.text();
346
+ const fileName = url.split("/").pop() || "unknown.fx";
347
+ return this.parse(content, fileName);
348
+ }
349
+ /**
350
+ * 解析FX文件内容
351
+ */
352
+ parse(content, fileName = "unknown.fx") {
353
+ const lines = content.split("\n");
354
+ const effect = {
355
+ fileName,
356
+ rawContent: content,
357
+ defines: [],
358
+ parameters: [],
359
+ staticVariables: [],
360
+ textures: [],
361
+ controllers: [],
362
+ includes: [],
363
+ techniques: [],
364
+ shaderFunctions: [],
365
+ comments: []
366
+ };
367
+ effect.defines = this.parseDefines(lines);
368
+ effect.parameters = this.parseParameters(lines);
369
+ effect.staticVariables = this.parseStaticVariables(lines);
370
+ effect.includes = this.parseIncludes(lines);
371
+ if (this.options.parseTextures) {
372
+ effect.textures = this.parseTextures(effect.defines);
373
+ }
374
+ if (this.options.parseControllers) {
375
+ effect.controllers = this.parseControllers(lines);
376
+ }
377
+ if (this.options.parseTechniques) {
378
+ effect.techniques = this.parseTechniques(content);
379
+ }
380
+ if (this.options.parseShaderFunctions) {
381
+ effect.shaderFunctions = this.parseShaderFunctions(content);
382
+ }
383
+ if (this.options.keepComments) {
384
+ effect.comments = this.parseComments(lines);
385
+ }
386
+ if (this.options.convertToGLSL) {
387
+ effect.glslShaders = this.convertShadersToGLSL(effect);
388
+ }
389
+ return effect;
390
+ }
391
+ /**
392
+ * 转换shader到GLSL
393
+ */
394
+ convertShadersToGLSL(effect) {
395
+ const warnings = [];
396
+ let vertexShaderName = this.options.vertexShaderFunction;
397
+ let fragmentShaderName = this.options.fragmentShaderFunction;
398
+ if (!vertexShaderName || !fragmentShaderName) {
399
+ const firstTechnique = effect.techniques[0];
400
+ if (firstTechnique?.passes && firstTechnique.passes.length > 0) {
401
+ const firstPass = firstTechnique.passes[0];
402
+ if (firstPass) {
403
+ vertexShaderName = vertexShaderName || firstPass.vertexShader?.function || void 0;
404
+ fragmentShaderName = fragmentShaderName || firstPass.pixelShader?.function || void 0;
405
+ }
406
+ }
407
+ }
408
+ if (!vertexShaderName || !fragmentShaderName) {
409
+ warnings.push("Could not determine shader function names. Skipping GLSL conversion.");
410
+ return {
411
+ warnings
412
+ };
413
+ }
414
+ const vsFunc = effect.shaderFunctions.find((f) => f.name === vertexShaderName);
415
+ const fsFunc = effect.shaderFunctions.find((f) => f.name === fragmentShaderName);
416
+ if (!vsFunc || !fsFunc) {
417
+ warnings.push(`Shader functions not found: ${vertexShaderName} or ${fragmentShaderName}`);
418
+ return {
419
+ warnings
420
+ };
421
+ }
422
+ const vertexResult = this.hlslConverter.convert(vsFunc.body, "vertex");
423
+ warnings.push(...vertexResult.warnings);
424
+ const fragmentResult = this.hlslConverter.convert(fsFunc.body, "fragment");
425
+ warnings.push(...fragmentResult.warnings);
426
+ return {
427
+ vertexShader: {
428
+ code: vertexResult.glslCode,
429
+ uniforms: vertexResult.uniforms,
430
+ attributes: vertexResult.attributes,
431
+ varyings: vertexResult.varyings
432
+ },
433
+ fragmentShader: {
434
+ code: fragmentResult.glslCode,
435
+ uniforms: fragmentResult.uniforms,
436
+ attributes: /* @__PURE__ */ new Map(),
437
+ varyings: fragmentResult.varyings
438
+ },
439
+ warnings
440
+ };
441
+ }
442
+ /**
443
+ * 解析宏定义 (#define)
444
+ */
445
+ parseDefines(lines) {
446
+ const defines = [];
447
+ const defineRegex = /^\s*(\/\/)?\s*#define\s+(\w+)(?:\s+(.+))?\s*$/;
448
+ lines.forEach((line, index) => {
449
+ const match = line.match(defineRegex);
450
+ if (match && match[2]) {
451
+ const isCommented = !!match[1];
452
+ const name = match[2];
453
+ const value = match[3]?.trim();
454
+ let comment;
455
+ if (value) {
456
+ const commentMatch = value.match(/\/\/(.+)$/);
457
+ if (commentMatch && commentMatch[1]) {
458
+ comment = commentMatch[1].trim();
459
+ }
460
+ }
461
+ const cleanValue = value?.replace(/\/\/.*$/, "").trim();
462
+ defines.push({
463
+ name,
464
+ value: cleanValue || void 0,
465
+ isCommented,
466
+ lineNumber: index + 1,
467
+ comment
468
+ });
469
+ }
470
+ });
471
+ return defines;
472
+ }
473
+ /**
474
+ * 解析参数声明
475
+ */
476
+ parseParameters(lines) {
477
+ const parameters = [];
478
+ const paramRegex = /^\s*(float|float2|float3|float4|float4x4|texture|sampler|sampler2D|bool|int)\s+(\w+)\s*(?::\s*(\w+))?\s*(?:<([^>]+)>)?\s*(?:=\s*([^;]+))?\s*;/;
479
+ lines.forEach((line, index) => {
480
+ const match = line.match(paramRegex);
481
+ if (match && match[1] && match[2]) {
482
+ const type = match[1];
483
+ const name = match[2];
484
+ const semantic = match[3];
485
+ const annotationsStr = match[4];
486
+ const defaultValue = match[5]?.trim();
487
+ let annotations;
488
+ if (annotationsStr) {
489
+ annotations = this.parseAnnotations(annotationsStr);
490
+ }
491
+ parameters.push({
492
+ type,
493
+ name,
494
+ semantic,
495
+ defaultValue,
496
+ annotations,
497
+ lineNumber: index + 1
498
+ });
499
+ }
500
+ });
501
+ return parameters;
502
+ }
503
+ /**
504
+ * 解析注解 (annotations)
505
+ */
506
+ parseAnnotations(annotationsStr) {
507
+ const annotations = {};
508
+ const annotationRegex = /(string|float|int|bool)\s+(\w+)\s*=\s*"?([^;"]+)"?/g;
509
+ let match;
510
+ while ((match = annotationRegex.exec(annotationsStr)) !== null) {
511
+ if (!match[1] || !match[2] || !match[3]) continue;
512
+ const type = match[1];
513
+ const name = match[2];
514
+ const value = match[3].trim();
515
+ if (type === "float") {
516
+ annotations[name] = parseFloat(value);
517
+ } else if (type === "int") {
518
+ annotations[name] = parseInt(value, 10);
519
+ } else if (type === "bool") {
520
+ annotations[name] = value === "true";
521
+ } else {
522
+ annotations[name] = value;
523
+ }
524
+ }
525
+ return annotations;
526
+ }
527
+ /**
528
+ * 解析静态变量
529
+ */
530
+ parseStaticVariables(lines) {
531
+ const variables = [];
532
+ const staticRegex = /^\s*static\s+(float|float2|float3|float4|bool|int)\s+(\w+)\s*=\s*(.+?);/;
533
+ lines.forEach((line, index) => {
534
+ const match = line.match(staticRegex);
535
+ if (match && match[1] && match[2] && match[3]) {
536
+ variables.push({
537
+ type: match[1],
538
+ name: match[2],
539
+ expression: match[3].trim(),
540
+ lineNumber: index + 1
541
+ });
542
+ }
543
+ });
544
+ return variables;
545
+ }
546
+ /**
547
+ * 解析纹理引用
548
+ */
549
+ parseTextures(defines) {
550
+ const textures = [];
551
+ const textureDefineRegex = /^BLEND(\w+)TEXTURE$/;
552
+ defines.forEach((define) => {
553
+ const match = define.name.match(textureDefineRegex);
554
+ if (match && match[1] && define.value && !define.isCommented) {
555
+ const purpose = match[1].toLowerCase();
556
+ const path = define.value.replace(/"/g, "");
557
+ const widthDefine = defines.find((d) => d.name === `${define.name}_X`);
558
+ const heightDefine = defines.find((d) => d.name === `${define.name}_Y`);
559
+ textures.push({
560
+ name: define.name,
561
+ path,
562
+ width: widthDefine?.value ? parseInt(widthDefine.value, 10) : void 0,
563
+ height: heightDefine?.value ? parseInt(heightDefine.value, 10) : void 0,
564
+ purpose
565
+ });
566
+ }
567
+ });
568
+ return textures;
569
+ }
570
+ /**
571
+ * 解析控制器引用 (CONTROLOBJECT)
572
+ */
573
+ parseControllers(lines) {
574
+ const controllers = [];
575
+ const controllerRegex = /(\w+)\s*:\s*CONTROLOBJECT\s*<\s*string\s+name\s*=\s*"([^"]+)"\s*;\s*string\s+item\s*=\s*"([^"]+)"\s*>/;
576
+ lines.forEach((line) => {
577
+ const match = line.match(controllerRegex);
578
+ if (match && match[1] && match[2] && match[3]) {
579
+ controllers.push({
580
+ name: match[1],
581
+ objectName: match[2],
582
+ itemName: match[3],
583
+ boundParameter: match[1]
584
+ });
585
+ }
586
+ });
587
+ return controllers;
588
+ }
589
+ /**
590
+ * 解析include指令
591
+ */
592
+ parseIncludes(lines) {
593
+ const includes = [];
594
+ const includeRegex = /^\s*#include\s+"([^"]+)"/;
595
+ lines.forEach((line) => {
596
+ const match = line.match(includeRegex);
597
+ if (match && match[1]) {
598
+ includes.push(match[1]);
599
+ }
600
+ });
601
+ return includes;
602
+ }
603
+ /**
604
+ * 解析Technique定义
605
+ */
606
+ parseTechniques(content) {
607
+ const techniques = [];
608
+ const techniqueRegex = /technique\s+(\w+)\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}/g;
609
+ let match;
610
+ while ((match = techniqueRegex.exec(content)) !== null) {
611
+ if (!match[1] || !match[2]) continue;
612
+ const name = match[1];
613
+ const body = match[2];
614
+ const lineNumber = content.substring(0, match.index).split("\n").length;
615
+ techniques.push({
616
+ name,
617
+ passes: this.parsePasses(body),
618
+ lineNumber
619
+ });
620
+ }
621
+ return techniques;
622
+ }
623
+ /**
624
+ * 解析Pass定义
625
+ */
626
+ parsePasses(techniqueBody) {
627
+ const passes = [];
628
+ const passRegex = /pass\s+(\w+)?\s*\{([^}]+)\}/g;
629
+ let match;
630
+ while ((match = passRegex.exec(techniqueBody)) !== null) {
631
+ if (!match[2]) continue;
632
+ const name = match[1] || void 0;
633
+ const body = match[2];
634
+ const pass = {
635
+ name,
636
+ renderStates: {}
637
+ };
638
+ const vsMatch = /VertexShader\s*=\s*compile\s+(\w+)\s+(\w+)/i.exec(body);
639
+ if (vsMatch && vsMatch[1] && vsMatch[2]) {
640
+ pass.vertexShader = {
641
+ profile: vsMatch[1],
642
+ function: vsMatch[2]
643
+ };
644
+ }
645
+ const psMatch = /PixelShader\s*=\s*compile\s+(\w+)\s+(\w+)/i.exec(body);
646
+ if (psMatch && psMatch[1] && psMatch[2]) {
647
+ pass.pixelShader = {
648
+ profile: psMatch[1],
649
+ function: psMatch[2]
650
+ };
651
+ }
652
+ const stateRegex = /(\w+)\s*=\s*([^;]+);/g;
653
+ let stateMatch;
654
+ while ((stateMatch = stateRegex.exec(body)) !== null) {
655
+ if (!stateMatch[1] || !stateMatch[2]) continue;
656
+ const stateName = stateMatch[1];
657
+ const stateValue = stateMatch[2].trim();
658
+ if (stateName !== "VertexShader" && stateName !== "PixelShader") {
659
+ pass.renderStates[stateName] = stateValue;
660
+ }
661
+ }
662
+ passes.push(pass);
663
+ }
664
+ return passes;
665
+ }
666
+ /**
667
+ * 解析着色器函数
668
+ */
669
+ parseShaderFunctions(content) {
670
+ const functions = [];
671
+ const functionRegex = /(struct|void|float|float2|float3|float4)\s+(\w+)\s*\(([^)]*)\)\s*(?::\s*(\w+))?\s*\{/g;
672
+ let match;
673
+ while ((match = functionRegex.exec(content)) !== null) {
674
+ if (!match[1] || !match[2] || !match[3]) continue;
675
+ const returnType = match[1];
676
+ const name = match[2];
677
+ const parameters = match[3];
678
+ const outputSemantic = match[4] || void 0;
679
+ const lineNumber = content.substring(0, match.index).split("\n").length;
680
+ const bodyStart = match.index + match[0].length;
681
+ let braceCount = 1;
682
+ let bodyEnd = bodyStart;
683
+ for (let i = bodyStart; i < content.length && braceCount > 0; i++) {
684
+ if (content[i] === "{") braceCount++;
685
+ if (content[i] === "}") braceCount--;
686
+ bodyEnd = i;
687
+ }
688
+ const body = content.substring(bodyStart, bodyEnd);
689
+ functions.push({
690
+ name,
691
+ returnType,
692
+ parameters,
693
+ body,
694
+ outputSemantic,
695
+ lineNumber
696
+ });
697
+ }
698
+ return functions;
699
+ }
700
+ /**
701
+ * 解析注释
702
+ */
703
+ parseComments(lines) {
704
+ const comments = [];
705
+ lines.forEach((line, index) => {
706
+ const lineCommentMatch = line.match(/^\s*\/\/(.*)$/);
707
+ if (lineCommentMatch && lineCommentMatch[1]) {
708
+ comments.push({
709
+ content: lineCommentMatch[1].trim(),
710
+ type: "line",
711
+ lineNumber: index + 1
712
+ });
713
+ }
714
+ const blockCommentMatch = line.match(/\/\*(.+?)\*\//);
715
+ if (blockCommentMatch && blockCommentMatch[1]) {
716
+ comments.push({
717
+ content: blockCommentMatch[1].trim(),
718
+ type: "block",
719
+ lineNumber: index + 1
720
+ });
721
+ }
722
+ });
723
+ return comments;
724
+ }
725
+ /**
726
+ * 生成FX文件摘要
727
+ */
728
+ generateSummary(effect) {
729
+ const enabledDefines = effect.defines.filter((d) => !d.isCommented).map((d) => d.name);
730
+ const disabledDefines = effect.defines.filter((d) => d.isCommented).map((d) => d.name);
731
+ return {
732
+ fileName: effect.fileName,
733
+ defineCount: effect.defines.length,
734
+ parameterCount: effect.parameters.length,
735
+ textureCount: effect.textures.length,
736
+ techniqueCount: effect.techniques.length,
737
+ hasLocalShadow: enabledDefines.includes("USE_LOCALSHADOW"),
738
+ hasExcellentShadow: enabledDefines.includes("USE_EXCELLENTSHADOW"),
739
+ hasHgShadow: enabledDefines.includes("USE_HGSHADOW"),
740
+ enabledDefines,
741
+ disabledDefines
742
+ };
743
+ }
744
+ /**
745
+ * 提取特定着色器函数的代码
746
+ */
747
+ extractShaderFunction(effect, functionName) {
748
+ const func = effect.shaderFunctions.find((f) => f.name === functionName);
749
+ if (!func) return null;
750
+ return `${func.returnType} ${func.name}(${func.parameters})${func.outputSemantic ? " : " + func.outputSemantic : ""}
751
+ {
752
+ ${func.body}
753
+ }`;
754
+ }
755
+ /**
756
+ * 获取所有启用的功能标志
757
+ */
758
+ getEnabledFeatures(effect) {
759
+ return effect.defines.filter((d) => !d.isCommented && !d.value).map((d) => d.name);
760
+ }
761
+ /**
762
+ * 获取配置参数
763
+ */
764
+ getConfigParameters(effect) {
765
+ return effect.parameters.filter(
766
+ (p) => p.annotations?.UIName || p.annotations?.UIWidget
767
+ );
768
+ }
769
+ };
770
+ var FXToThreeAdapter = class {
771
+ constructor(effect, basePath = "") {
772
+ this.effect = effect;
773
+ this.basePath = basePath;
774
+ this.textureLoader = new THREE3.TextureLoader();
775
+ this.loadedTextures = /* @__PURE__ */ new Map();
776
+ }
777
+ /**
778
+ * 创建ShaderMaterial(如果有GLSL shader)
779
+ */
780
+ createShaderMaterial() {
781
+ if (!this.effect.glslShaders?.vertexShader || !this.effect.glslShaders?.fragmentShader) {
782
+ console.warn("[FXToThreeAdapter] No GLSL shaders available. Use convertToGLSL option in FXParser.");
783
+ return null;
784
+ }
785
+ const vs = this.effect.glslShaders.vertexShader;
786
+ const fs = this.effect.glslShaders.fragmentShader;
787
+ const uniforms = {};
788
+ vs.uniforms.forEach((info, name) => {
789
+ uniforms[name] = { value: this.getDefaultUniformValue(info.glslType) };
790
+ });
791
+ fs.uniforms.forEach((info, name) => {
792
+ if (!uniforms[name]) {
793
+ uniforms[name] = { value: this.getDefaultUniformValue(info.glslType) };
794
+ }
795
+ });
796
+ uniforms.modelMatrix = { value: new THREE3.Matrix4() };
797
+ uniforms.viewMatrix = { value: new THREE3.Matrix4() };
798
+ uniforms.projectionMatrix = { value: new THREE3.Matrix4() };
799
+ uniforms.normalMatrix = { value: new THREE3.Matrix3() };
800
+ uniforms.cameraPosition = { value: new THREE3.Vector3() };
801
+ const material = new THREE3.ShaderMaterial({
802
+ vertexShader: vs.code,
803
+ fragmentShader: fs.code,
804
+ uniforms
805
+ });
806
+ console.log("[FXToThreeAdapter] Created ShaderMaterial with", Object.keys(uniforms).length, "uniforms");
807
+ console.log("[FXToThreeAdapter] Conversion warnings:", this.effect.glslShaders.warnings);
808
+ return material;
809
+ }
810
+ /**
811
+ * 获取uniform的默认值
812
+ */
813
+ getDefaultUniformValue(glslType) {
814
+ switch (glslType) {
815
+ case "float":
816
+ return 0;
817
+ case "vec2":
818
+ return new THREE3.Vector2(0, 0);
819
+ case "vec3":
820
+ return new THREE3.Vector3(0, 0, 0);
821
+ case "vec4":
822
+ return new THREE3.Vector4(0, 0, 0, 0);
823
+ case "mat3":
824
+ return new THREE3.Matrix3();
825
+ case "mat4":
826
+ return new THREE3.Matrix4();
827
+ case "sampler2D":
828
+ return null;
829
+ case "samplerCube":
830
+ return null;
831
+ default:
832
+ return null;
833
+ }
834
+ }
835
+ /**
836
+ * 提取材质配置
837
+ */
838
+ extractMaterialConfig() {
839
+ const config = {
840
+ uniforms: {}
841
+ };
842
+ console.log("[FXToThreeAdapter] Extracting material config from FX:", this.effect.fileName);
843
+ console.log("[FXToThreeAdapter] Total parameters:", this.effect.parameters.length);
844
+ this.effect.parameters.forEach((param) => {
845
+ const name = param.name.toLowerCase();
846
+ if (name.startsWith("add")) {
847
+ console.log(`[FXToThreeAdapter] Skipping additive param "${param.name}" (not an absolute value)`);
848
+ return;
849
+ }
850
+ if (name.includes("materialrgb") || name.includes("material")) {
851
+ const colorValue = this.parseFloat3(param.defaultValue);
852
+ if (colorValue) {
853
+ config.color = new THREE3.Color(
854
+ colorValue[0],
855
+ colorValue[1],
856
+ colorValue[2]
857
+ );
858
+ console.log(`[FXToThreeAdapter] Found color param "${param.name}":`, colorValue);
859
+ }
860
+ }
861
+ if (name.includes("emissive")) {
862
+ const emissiveValue = this.parseFloat3(param.defaultValue);
863
+ if (emissiveValue) {
864
+ config.emissive = new THREE3.Color(
865
+ emissiveValue[0],
866
+ emissiveValue[1],
867
+ emissiveValue[2]
868
+ );
869
+ console.log(`[FXToThreeAdapter] Found emissive param "${param.name}":`, emissiveValue);
870
+ }
871
+ }
872
+ if (name.includes("specular")) {
873
+ const specularValue = this.parseFloat3(param.defaultValue);
874
+ if (specularValue) {
875
+ config.specular = new THREE3.Color(
876
+ specularValue[0],
877
+ specularValue[1],
878
+ specularValue[2]
879
+ );
880
+ console.log(`[FXToThreeAdapter] Found specular param "${param.name}":`, specularValue);
881
+ }
882
+ }
883
+ if (name.includes("shininess") || name.includes("specularpower")) {
884
+ const shininessValue = this.parseFloat(param.defaultValue);
885
+ if (shininessValue !== null) {
886
+ config.shininess = shininessValue;
887
+ console.log(`[FXToThreeAdapter] Found shininess param "${param.name}":`, shininessValue);
888
+ }
889
+ }
890
+ if (config.uniforms && param.defaultValue) {
891
+ config.uniforms[param.name] = {
892
+ value: this.parseParameterValue(param)
893
+ };
894
+ }
895
+ });
896
+ console.log("[FXToThreeAdapter] Final material config:", {
897
+ color: config.color,
898
+ emissive: config.emissive,
899
+ specular: config.specular,
900
+ shininess: config.shininess
901
+ });
902
+ return config;
903
+ }
904
+ /**
905
+ * 提取渲染配置
906
+ */
907
+ extractRenderConfig() {
908
+ const config = {};
909
+ const hasLocalShadow = this.effect.defines.some(
910
+ (d) => d.name === "USE_LOCALSHADOW" && !d.isCommented
911
+ );
912
+ const hasExcellentShadow = this.effect.defines.some(
913
+ (d) => d.name === "USE_EXCELLENTSHADOW" && !d.isCommented
914
+ );
915
+ const hasHgShadow = this.effect.defines.some(
916
+ (d) => d.name === "USE_HGSHADOW" && !d.isCommented
917
+ );
918
+ config.enableShadow = hasLocalShadow || hasExcellentShadow || hasHgShadow;
919
+ const shadowMapSizeDefine = this.effect.defines.find(
920
+ (d) => d.name === "LS_ShadowMapBuffSize"
921
+ );
922
+ if (shadowMapSizeDefine?.value) {
923
+ config.shadowMapSize = parseInt(shadowMapSizeDefine.value, 10);
924
+ }
925
+ const lightDirDefine = this.effect.defines.find(
926
+ (d) => d.name === "LS_InitDirection"
927
+ );
928
+ if (lightDirDefine?.value) {
929
+ const dir = this.parseFloat3(lightDirDefine.value);
930
+ if (dir) {
931
+ config.lightDirection = new THREE3.Vector3(dir[0], dir[1], dir[2]).normalize();
932
+ }
933
+ }
934
+ const hasToon = this.effect.defines.some(
935
+ (d) => d.name === "MODEL_TOON" && !d.isCommented
936
+ );
937
+ config.toneMapping = hasToon ? THREE3.NoToneMapping : THREE3.ACESFilmicToneMapping;
938
+ config.toneMappingExposure = 1;
939
+ console.log("[FXToThreeAdapter] Extracted render config:", config);
940
+ return config;
941
+ }
942
+ /**
943
+ * 加载纹理
944
+ */
945
+ async loadTextures() {
946
+ const promises = this.effect.textures.map(async (fxTexture) => {
947
+ const path = this.basePath ? `${this.basePath}/${fxTexture.path}` : fxTexture.path;
948
+ try {
949
+ const texture = await this.loadTexture(path);
950
+ this.loadedTextures.set(fxTexture.name, texture);
951
+ return { name: fxTexture.name, texture };
952
+ } catch (error) {
953
+ console.warn(`Failed to load texture ${fxTexture.name}:`, error);
954
+ return null;
955
+ }
956
+ });
957
+ await Promise.all(promises);
958
+ return this.loadedTextures;
959
+ }
960
+ /**
961
+ * 加载单个纹理
962
+ */
963
+ loadTexture(path) {
964
+ return new Promise((resolve, reject) => {
965
+ this.textureLoader.load(
966
+ path,
967
+ (texture) => {
968
+ texture.wrapS = THREE3.RepeatWrapping;
969
+ texture.wrapT = THREE3.RepeatWrapping;
970
+ texture.needsUpdate = true;
971
+ resolve(texture);
972
+ },
973
+ void 0,
974
+ (error) => reject(error)
975
+ );
976
+ });
977
+ }
978
+ /**
979
+ * 创建Three.js材质
980
+ */
981
+ createMaterial() {
982
+ const config = this.extractMaterialConfig();
983
+ const material = new THREE3.MeshPhongMaterial({
984
+ color: config.color || 16777215,
985
+ emissive: config.emissive || 0,
986
+ specular: config.specular || 1118481,
987
+ shininess: config.shininess ?? 30,
988
+ transparent: config.transparent ?? false,
989
+ opacity: config.opacity ?? 1
990
+ });
991
+ const diffuseTexture = this.getTextureByPurpose("diffuse");
992
+ if (diffuseTexture) {
993
+ material.map = diffuseTexture;
994
+ }
995
+ const normalTexture = this.getTextureByPurpose("normal");
996
+ if (normalTexture) {
997
+ material.normalMap = normalTexture;
998
+ }
999
+ return material;
1000
+ }
1001
+ /**
1002
+ * 配置Three.js场景
1003
+ * 仅应用渲染器配置,不添加光源
1004
+ */
1005
+ configureScene(scene, renderer) {
1006
+ const renderConfig = this.extractRenderConfig();
1007
+ if (renderConfig.enableShadow !== void 0) {
1008
+ renderer.shadowMap.enabled = renderConfig.enableShadow;
1009
+ if (renderConfig.enableShadow) {
1010
+ renderer.shadowMap.type = THREE3.PCFSoftShadowMap;
1011
+ }
1012
+ }
1013
+ if (renderConfig.toneMapping !== void 0) {
1014
+ renderer.toneMapping = renderConfig.toneMapping;
1015
+ }
1016
+ if (renderConfig.toneMappingExposure !== void 0) {
1017
+ renderer.toneMappingExposure = renderConfig.toneMappingExposure;
1018
+ }
1019
+ }
1020
+ /**
1021
+ * 获取指定用途的纹理
1022
+ */
1023
+ getTextureByPurpose(purpose) {
1024
+ const fxTexture = this.effect.textures.find(
1025
+ (t) => t.purpose?.toLowerCase().includes(purpose.toLowerCase())
1026
+ );
1027
+ if (!fxTexture) return null;
1028
+ return this.loadedTextures.get(fxTexture.name) || null;
1029
+ }
1030
+ /**
1031
+ * 解析float3值
1032
+ */
1033
+ parseFloat3(value) {
1034
+ if (!value) return null;
1035
+ const match = value.match(/float3\s*\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*\)/);
1036
+ if (!match || !match[1] || !match[2] || !match[3]) return null;
1037
+ return [
1038
+ parseFloat(match[1]),
1039
+ parseFloat(match[2]),
1040
+ parseFloat(match[3])
1041
+ ];
1042
+ }
1043
+ /**
1044
+ * 解析float值
1045
+ */
1046
+ parseFloat(value) {
1047
+ if (!value) return null;
1048
+ const num = parseFloat(value);
1049
+ return isNaN(num) ? null : num;
1050
+ }
1051
+ /**
1052
+ * 解析参数值
1053
+ */
1054
+ parseParameterValue(param) {
1055
+ if (!param.defaultValue) return null;
1056
+ switch (param.type) {
1057
+ case "float":
1058
+ return this.parseFloat(param.defaultValue);
1059
+ case "float2": {
1060
+ const match = param.defaultValue.match(/float2\s*\(\s*([\d.]+)\s*,\s*([\d.]+)\s*\)/);
1061
+ return match && match[1] && match[2] ? new THREE3.Vector2(parseFloat(match[1]), parseFloat(match[2])) : null;
1062
+ }
1063
+ case "float3": {
1064
+ const values = this.parseFloat3(param.defaultValue);
1065
+ return values ? new THREE3.Vector3(values[0], values[1], values[2]) : null;
1066
+ }
1067
+ case "float4": {
1068
+ const match = param.defaultValue.match(/float4\s*\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*\)/);
1069
+ if (match && match[1] && match[2] && match[3] && match[4]) {
1070
+ return new THREE3.Vector4(
1071
+ parseFloat(match[1]),
1072
+ parseFloat(match[2]),
1073
+ parseFloat(match[3]),
1074
+ parseFloat(match[4])
1075
+ );
1076
+ }
1077
+ return null;
1078
+ }
1079
+ default:
1080
+ return param.defaultValue;
1081
+ }
1082
+ }
1083
+ /**
1084
+ * 获取所有自定义uniforms
1085
+ */
1086
+ getUniforms() {
1087
+ const uniforms = {};
1088
+ this.effect.parameters.forEach((param) => {
1089
+ const value = this.parseParameterValue(param);
1090
+ if (value !== null) {
1091
+ uniforms[param.name] = { value };
1092
+ }
1093
+ });
1094
+ this.loadedTextures.forEach((texture, name) => {
1095
+ uniforms[name] = { value: texture };
1096
+ });
1097
+ return uniforms;
1098
+ }
1099
+ /**
1100
+ * 生成配置摘要
1101
+ */
1102
+ getSummary() {
1103
+ return {
1104
+ materialParams: this.effect.parameters.map((p) => `${p.type} ${p.name}`),
1105
+ textures: this.effect.textures.map((t) => t.path),
1106
+ renderFeatures: this.effect.defines.filter((d) => !d.isCommented && (d.name.startsWith("USE_") || d.name.includes("SHADOW"))).map((d) => d.name)
1107
+ };
1108
+ }
1109
+ };
1110
+ var MultiFXAdapter = class {
1111
+ constructor(options = {}) {
1112
+ this.effects = /* @__PURE__ */ new Map();
1113
+ this.adapters = /* @__PURE__ */ new Map();
1114
+ this.configs = [];
1115
+ this.options = {
1116
+ mergeStrategy: options.mergeStrategy || "override",
1117
+ autoLoadTextures: options.autoLoadTextures ?? true
1118
+ };
1119
+ this.parser = new FXParser();
1120
+ }
1121
+ /**
1122
+ * 识别文件类型
1123
+ */
1124
+ detectFileType(path, configType) {
1125
+ if (configType && configType !== "auto") {
1126
+ return configType;
1127
+ }
1128
+ const ext = path.toLowerCase().split(".").pop();
1129
+ if (ext === "x") return "x";
1130
+ if (ext === "fx") return "fx";
1131
+ return "fx";
1132
+ }
1133
+ /**
1134
+ * 添加效果文件(支持.fx和.x)
1135
+ */
1136
+ async addFX(config) {
1137
+ if (!config.enabled && config.enabled !== void 0) {
1138
+ console.log("[MultiFXAdapter] Effect disabled:", config.path);
1139
+ return;
1140
+ }
1141
+ try {
1142
+ const fileType = this.detectFileType(config.path, config.type);
1143
+ const desc = config.description || config.path;
1144
+ console.log(`[MultiFXAdapter] Loading ${fileType.toUpperCase()} file:`, desc);
1145
+ const effect = await this.parser.loadAndParse(config.path);
1146
+ this.effects.set(config.path, effect);
1147
+ const adapter = new FXToThreeAdapter(effect, config.texturePath || "");
1148
+ this.adapters.set(config.path, adapter);
1149
+ if (this.options.autoLoadTextures) {
1150
+ console.log(`[MultiFXAdapter] Loading textures for ${fileType}:`, desc);
1151
+ await adapter.loadTextures();
1152
+ }
1153
+ const defaultPriority = fileType === "x" ? -10 : 0;
1154
+ const defaultTarget = fileType === "x" ? "all" : "model";
1155
+ this.configs.push({
1156
+ ...config,
1157
+ type: fileType,
1158
+ priority: config.priority ?? defaultPriority,
1159
+ target: config.target ?? defaultTarget,
1160
+ enabled: true
1161
+ });
1162
+ this.configs.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
1163
+ const lastConfig = this.configs[this.configs.length - 1];
1164
+ console.log(`[MultiFXAdapter] ${fileType.toUpperCase()} loaded successfully:`, desc);
1165
+ console.log(`[MultiFXAdapter] - Priority: ${lastConfig?.priority ?? 0}`);
1166
+ console.log(`[MultiFXAdapter] - Target: ${lastConfig?.target ?? "all"}`);
1167
+ } catch (error) {
1168
+ console.error("[MultiFXAdapter] Failed to load effect file:", config.path, error);
1169
+ throw error;
1170
+ }
1171
+ }
1172
+ /**
1173
+ * 批量添加FX文件
1174
+ */
1175
+ async addMultipleFX(configs) {
1176
+ await Promise.all(configs.map((config) => this.addFX(config)));
1177
+ }
1178
+ /**
1179
+ * 移除FX文件
1180
+ */
1181
+ removeFX(path) {
1182
+ this.effects.delete(path);
1183
+ this.adapters.delete(path);
1184
+ this.configs = this.configs.filter((c) => c.path !== path);
1185
+ }
1186
+ /**
1187
+ * 清空所有FX
1188
+ */
1189
+ clear() {
1190
+ this.effects.clear();
1191
+ this.adapters.clear();
1192
+ this.configs = [];
1193
+ }
1194
+ /**
1195
+ * 合并材质配置
1196
+ */
1197
+ extractMergedMaterialConfig(target) {
1198
+ const merged = {
1199
+ uniforms: {}
1200
+ };
1201
+ console.log("[MultiFXAdapter] Extracting material config for target:", target);
1202
+ console.log("[MultiFXAdapter] Total configs:", this.configs.length);
1203
+ this.configs.forEach((config) => {
1204
+ let shouldApply = false;
1205
+ if (!target || config.target === "all") {
1206
+ shouldApply = true;
1207
+ } else if (Array.isArray(config.target)) {
1208
+ shouldApply = config.target.includes(target);
1209
+ } else {
1210
+ shouldApply = config.target === target;
1211
+ }
1212
+ if (!shouldApply) {
1213
+ console.log(`[MultiFXAdapter] - Skipping ${config.description || config.path} (target mismatch: ${config.target} !== ${target})`);
1214
+ return;
1215
+ }
1216
+ const adapter = this.adapters.get(config.path);
1217
+ if (!adapter) {
1218
+ console.log(`[MultiFXAdapter] - Skipping ${config.description || config.path} (adapter not found)`);
1219
+ return;
1220
+ }
1221
+ console.log(`[MultiFXAdapter] \u2705 Applying ${config.description || config.path} (priority: ${config.priority})`);
1222
+ const materialConfig = adapter.extractMaterialConfig();
1223
+ switch (this.options.mergeStrategy) {
1224
+ case "override":
1225
+ if (materialConfig.color) merged.color = materialConfig.color;
1226
+ if (materialConfig.emissive) merged.emissive = materialConfig.emissive;
1227
+ if (materialConfig.specular) merged.specular = materialConfig.specular;
1228
+ if (materialConfig.shininess !== void 0) merged.shininess = materialConfig.shininess;
1229
+ if (materialConfig.opacity !== void 0) merged.opacity = materialConfig.opacity;
1230
+ if (materialConfig.transparent !== void 0) merged.transparent = materialConfig.transparent;
1231
+ if (materialConfig.uniforms && merged.uniforms) {
1232
+ Object.assign(merged.uniforms, materialConfig.uniforms);
1233
+ }
1234
+ break;
1235
+ case "merge":
1236
+ merged.color = materialConfig.color || merged.color;
1237
+ merged.emissive = materialConfig.emissive || merged.emissive;
1238
+ merged.specular = materialConfig.specular || merged.specular;
1239
+ merged.shininess = materialConfig.shininess ?? merged.shininess;
1240
+ merged.opacity = materialConfig.opacity ?? merged.opacity;
1241
+ merged.transparent = materialConfig.transparent ?? merged.transparent;
1242
+ if (materialConfig.uniforms && merged.uniforms) {
1243
+ Object.assign(merged.uniforms, materialConfig.uniforms);
1244
+ }
1245
+ break;
1246
+ case "additive":
1247
+ if (materialConfig.color && merged.color) {
1248
+ merged.color.add(materialConfig.color);
1249
+ } else {
1250
+ merged.color = materialConfig.color;
1251
+ }
1252
+ if (materialConfig.emissive && merged.emissive) {
1253
+ merged.emissive.add(materialConfig.emissive);
1254
+ } else {
1255
+ merged.emissive = materialConfig.emissive;
1256
+ }
1257
+ if (materialConfig.shininess !== void 0) {
1258
+ merged.shininess = (merged.shininess ?? 0) + materialConfig.shininess;
1259
+ }
1260
+ if (materialConfig.uniforms && merged.uniforms) {
1261
+ Object.assign(merged.uniforms, materialConfig.uniforms);
1262
+ }
1263
+ break;
1264
+ }
1265
+ });
1266
+ return merged;
1267
+ }
1268
+ /**
1269
+ * 合并渲染配置
1270
+ */
1271
+ extractMergedRenderConfig() {
1272
+ const merged = {};
1273
+ console.log("[MultiFXAdapter] Extracting merged render config from", this.configs.length, "configs");
1274
+ this.configs.forEach((config) => {
1275
+ const adapter = this.adapters.get(config.path);
1276
+ if (!adapter) return;
1277
+ const renderConfig = adapter.extractRenderConfig();
1278
+ console.log(`[MultiFXAdapter] Processing ${config.description || config.path}:`);
1279
+ console.log(" - enableShadow:", renderConfig.enableShadow);
1280
+ console.log(" - shadowMapSize:", renderConfig.shadowMapSize);
1281
+ console.log(" - toneMapping:", renderConfig.toneMapping);
1282
+ if (renderConfig.enableShadow !== void 0) {
1283
+ merged.enableShadow = renderConfig.enableShadow;
1284
+ }
1285
+ if (renderConfig.shadowMapSize !== void 0) {
1286
+ merged.shadowMapSize = renderConfig.shadowMapSize;
1287
+ }
1288
+ if (renderConfig.ambientLightIntensity !== void 0) {
1289
+ merged.ambientLightIntensity = renderConfig.ambientLightIntensity;
1290
+ }
1291
+ if (renderConfig.directionalLightIntensity !== void 0) {
1292
+ merged.directionalLightIntensity = renderConfig.directionalLightIntensity;
1293
+ }
1294
+ if (renderConfig.lightDirection) {
1295
+ merged.lightDirection = renderConfig.lightDirection;
1296
+ }
1297
+ if (renderConfig.toneMapping !== void 0) {
1298
+ merged.toneMapping = renderConfig.toneMapping;
1299
+ console.log(`[MultiFXAdapter] \u2705 ToneMapping set to:`, renderConfig.toneMapping === THREE3.NoToneMapping ? "NoToneMapping (Toon)" : "ACESFilmicToneMapping");
1300
+ }
1301
+ if (renderConfig.toneMappingExposure !== void 0) {
1302
+ merged.toneMappingExposure = renderConfig.toneMappingExposure;
1303
+ }
1304
+ });
1305
+ console.log("[MultiFXAdapter] Final merged render config:", merged);
1306
+ return merged;
1307
+ }
1308
+ /**
1309
+ * 应用到Three.js材质
1310
+ * 支持MeshPhongMaterial和MeshToonMaterial(MMD常用)
1311
+ */
1312
+ applyToMaterial(material, target) {
1313
+ if (!(material instanceof THREE3.MeshPhongMaterial || material instanceof THREE3.MeshToonMaterial)) {
1314
+ console.warn("[MultiFXAdapter] Material type not supported:", material.type);
1315
+ return;
1316
+ }
1317
+ console.log("[MultiFXAdapter] Applying to material type:", material.type);
1318
+ const config = this.extractMergedMaterialConfig(target);
1319
+ console.log("[MultiFXAdapter] Extracted material config for target:", target);
1320
+ console.log(" - color:", config.color);
1321
+ console.log(" - emissive:", config.emissive);
1322
+ console.log(" - specular:", config.specular);
1323
+ console.log(" - shininess:", config.shininess);
1324
+ let applied = false;
1325
+ if (config.color) {
1326
+ const isBlack = config.color.r === 0 && config.color.g === 0 && config.color.b === 0;
1327
+ if (!isBlack) {
1328
+ material.color.copy(config.color);
1329
+ applied = true;
1330
+ } else {
1331
+ console.log("[MultiFXAdapter] Skipping black color (0,0,0) to preserve original material");
1332
+ }
1333
+ }
1334
+ if (config.emissive && material.emissive) {
1335
+ const isBlack = config.emissive.r === 0 && config.emissive.g === 0 && config.emissive.b === 0;
1336
+ if (!isBlack) {
1337
+ material.emissive.copy(config.emissive);
1338
+ applied = true;
1339
+ }
1340
+ }
1341
+ if (config.specular && material.specular) {
1342
+ material.specular.copy(config.specular);
1343
+ applied = true;
1344
+ console.log("[MultiFXAdapter] Applied specular");
1345
+ }
1346
+ if (config.shininess !== void 0 && material.shininess !== void 0) {
1347
+ material.shininess = config.shininess;
1348
+ applied = true;
1349
+ console.log("[MultiFXAdapter] Applied shininess:", config.shininess);
1350
+ }
1351
+ if (config.opacity !== void 0) {
1352
+ material.opacity = config.opacity;
1353
+ applied = true;
1354
+ }
1355
+ if (config.transparent !== void 0) {
1356
+ material.transparent = config.transparent;
1357
+ applied = true;
1358
+ }
1359
+ if (applied) {
1360
+ console.log("[MultiFXAdapter] \u2705 Material config applied to:", target || "default");
1361
+ } else {
1362
+ console.warn("[MultiFXAdapter] \u26A0\uFE0F No material config to apply (all values are undefined)");
1363
+ }
1364
+ }
1365
+ /**
1366
+ * 创建ShaderMaterial(如果配置了useShaderMaterial)
1367
+ * 返回第一个匹配target的ShaderMaterial,如果没有则返回null
1368
+ */
1369
+ createShaderMaterial(target) {
1370
+ const shaderConfig = this.configs.find((config) => {
1371
+ if (!config.useShaderMaterial) return false;
1372
+ if (!target || config.target === "all") return true;
1373
+ if (Array.isArray(config.target)) return config.target.includes(target);
1374
+ return config.target === target;
1375
+ });
1376
+ if (!shaderConfig) {
1377
+ console.log(`[MultiFXAdapter] No shader material config found for target: ${target}`);
1378
+ return null;
1379
+ }
1380
+ const adapter = this.adapters.get(shaderConfig.path);
1381
+ if (!adapter) {
1382
+ console.warn(`[MultiFXAdapter] Adapter not found for: ${shaderConfig.path}`);
1383
+ return null;
1384
+ }
1385
+ const material = adapter.createShaderMaterial();
1386
+ if (material) {
1387
+ console.log(`[MultiFXAdapter] \u2705 Created ShaderMaterial for target: ${target} from ${shaderConfig.description || shaderConfig.path}`);
1388
+ }
1389
+ return material;
1390
+ }
1391
+ /**
1392
+ * 应用到Three.js场景
1393
+ */
1394
+ applyToScene(scene, renderer) {
1395
+ const renderConfig = this.extractMergedRenderConfig();
1396
+ console.log("[MultiFXAdapter] Applying render config:");
1397
+ console.log(" - enableShadow:", renderConfig.enableShadow);
1398
+ console.log(" - shadowMapSize:", renderConfig.shadowMapSize);
1399
+ console.log(" - toneMapping:", renderConfig.toneMapping);
1400
+ console.log(" - ambientLightIntensity:", renderConfig.ambientLightIntensity);
1401
+ console.log(" - directionalLightIntensity:", renderConfig.directionalLightIntensity);
1402
+ if (renderConfig.enableShadow !== void 0) {
1403
+ renderer.shadowMap.enabled = renderConfig.enableShadow;
1404
+ if (renderConfig.enableShadow) {
1405
+ renderer.shadowMap.type = THREE3.PCFSoftShadowMap;
1406
+ }
1407
+ console.log("[MultiFXAdapter] \u2705 Shadow config applied:", renderConfig.enableShadow);
1408
+ }
1409
+ if (renderConfig.toneMapping !== void 0) {
1410
+ renderer.toneMapping = renderConfig.toneMapping;
1411
+ const toneMappingName = renderConfig.toneMapping === THREE3.NoToneMapping ? "NoToneMapping" : "ACESFilmicToneMapping";
1412
+ console.log("[MultiFXAdapter] \u2705 ToneMapping applied:", toneMappingName);
1413
+ }
1414
+ if (renderConfig.toneMappingExposure !== void 0) {
1415
+ renderer.toneMappingExposure = renderConfig.toneMappingExposure;
1416
+ }
1417
+ let ambientLight = scene.children.find((obj) => obj instanceof THREE3.AmbientLight);
1418
+ let directionalLight = scene.children.find((obj) => obj instanceof THREE3.DirectionalLight);
1419
+ console.log("[MultiFXAdapter] Found lights in scene:", {
1420
+ ambientLight: !!ambientLight,
1421
+ directionalLight: !!directionalLight
1422
+ });
1423
+ if (renderConfig.ambientLightIntensity !== void 0 && ambientLight) {
1424
+ console.log("[MultiFXAdapter] Updating ambient light intensity:", renderConfig.ambientLightIntensity);
1425
+ ambientLight.intensity = renderConfig.ambientLightIntensity;
1426
+ }
1427
+ if (renderConfig.directionalLightIntensity !== void 0 && directionalLight) {
1428
+ console.log("[MultiFXAdapter] Updating directional light intensity:", renderConfig.directionalLightIntensity);
1429
+ directionalLight.intensity = renderConfig.directionalLightIntensity;
1430
+ }
1431
+ if (renderConfig.lightDirection && directionalLight) {
1432
+ directionalLight.position.copy(renderConfig.lightDirection).multiplyScalar(10);
1433
+ console.log("[MultiFXAdapter] Updated light direction");
1434
+ }
1435
+ if (renderConfig.shadowMapSize && directionalLight?.shadow) {
1436
+ directionalLight.shadow.mapSize.width = renderConfig.shadowMapSize;
1437
+ directionalLight.shadow.mapSize.height = renderConfig.shadowMapSize;
1438
+ console.log("[MultiFXAdapter] Updated shadow map size:", renderConfig.shadowMapSize);
1439
+ }
1440
+ console.log("[MultiFXAdapter] \u2705 Scene config applied");
1441
+ }
1442
+ /**
1443
+ * 获取所有已加载的FX效果
1444
+ */
1445
+ getLoadedEffects() {
1446
+ return Array.from(this.effects.values());
1447
+ }
1448
+ /**
1449
+ * 获取配置摘要
1450
+ */
1451
+ getSummary() {
1452
+ const xCount = this.configs.filter((c) => c.type === "x").length;
1453
+ const fxCount = this.configs.filter((c) => c.type === "fx").length;
1454
+ return {
1455
+ totalFX: this.effects.size,
1456
+ enabledFX: this.configs.length,
1457
+ xFiles: xCount,
1458
+ fxFiles: fxCount,
1459
+ configs: this.configs.map((config) => {
1460
+ const adapter = this.adapters.get(config.path);
1461
+ const summary = adapter?.getSummary();
1462
+ return {
1463
+ path: config.path,
1464
+ type: config.type ?? "fx",
1465
+ priority: config.priority ?? 0,
1466
+ target: config.target ?? "all",
1467
+ features: summary?.renderFeatures || [],
1468
+ description: config.description
1469
+ };
1470
+ })
1471
+ };
1472
+ }
1473
+ /**
1474
+ * 获取指定类型的配置
1475
+ */
1476
+ getConfigsByType(type) {
1477
+ return this.configs.filter((c) => c.type === type);
1478
+ }
1479
+ /**
1480
+ * 获取场景级效果(.x文件)
1481
+ */
1482
+ getSceneEffects() {
1483
+ return this.getConfigsByType("x");
1484
+ }
1485
+ /**
1486
+ * 获取模型级效果(.fx文件)
1487
+ */
1488
+ getModelEffects() {
1489
+ return this.getConfigsByType("fx");
1490
+ }
1491
+ /**
1492
+ * 获取合并后的Uniforms
1493
+ */
1494
+ getMergedUniforms(target) {
1495
+ const merged = {};
1496
+ this.configs.forEach((config) => {
1497
+ if (target && config.target !== "all") {
1498
+ if (Array.isArray(config.target)) {
1499
+ if (!config.target.includes(target)) return;
1500
+ } else if (config.target !== target) {
1501
+ return;
1502
+ }
1503
+ }
1504
+ const adapter = this.adapters.get(config.path);
1505
+ if (!adapter) return;
1506
+ const uniforms = adapter.getUniforms();
1507
+ Object.assign(merged, uniforms);
1508
+ });
1509
+ return merged;
1510
+ }
1511
+ /**
1512
+ * 分层应用到场景对象
1513
+ * @param scene Three.js场景
1514
+ * @param renderer Three.js渲染器
1515
+ * @param modelMeshes 模型网格数组(可选,用于精确控制)
1516
+ * @param stageMeshes 舞台网格数组(可选,用于精确控制)
1517
+ */
1518
+ applyLayered(scene, renderer, modelMeshes, stageMeshes) {
1519
+ console.log("[MultiFXAdapter] Applying layered effects...");
1520
+ const sceneEffects = this.getSceneEffects();
1521
+ console.log(`[MultiFXAdapter] Applying ${sceneEffects.length} scene-level effects (.x files)`);
1522
+ sceneEffects.forEach((config) => {
1523
+ const adapter = this.adapters.get(config.path);
1524
+ if (adapter) {
1525
+ console.log(`[MultiFXAdapter] - Applying: ${config.description || config.path}`);
1526
+ const renderConfig = adapter.extractRenderConfig();
1527
+ if (renderConfig.toneMapping !== void 0) {
1528
+ renderer.toneMapping = renderConfig.toneMapping;
1529
+ }
1530
+ if (renderConfig.toneMappingExposure !== void 0) {
1531
+ renderer.toneMappingExposure = renderConfig.toneMappingExposure;
1532
+ }
1533
+ if (renderConfig.enableShadow !== void 0) {
1534
+ renderer.shadowMap.enabled = renderConfig.enableShadow;
1535
+ }
1536
+ scene.traverse((obj) => {
1537
+ if (obj instanceof THREE3.Mesh && obj.material instanceof THREE3.MeshPhongMaterial) {
1538
+ this.applyToMaterial(obj.material, "scene");
1539
+ }
1540
+ });
1541
+ }
1542
+ });
1543
+ const modelEffects = this.getModelEffects();
1544
+ console.log(`[MultiFXAdapter] Applying ${modelEffects.length} model-level effects (.fx files)`);
1545
+ modelEffects.forEach((config) => {
1546
+ console.log(`[MultiFXAdapter] - Applying: ${config.description || config.path}`);
1547
+ if (modelMeshes) {
1548
+ modelMeshes.forEach((mesh) => {
1549
+ if (mesh instanceof THREE3.Mesh && mesh.material instanceof THREE3.MeshPhongMaterial) {
1550
+ this.applyToMaterial(mesh.material, "model");
1551
+ }
1552
+ mesh.traverse((obj) => {
1553
+ if (obj instanceof THREE3.Mesh && obj.material instanceof THREE3.MeshPhongMaterial) {
1554
+ this.applyToMaterial(obj.material, "model");
1555
+ }
1556
+ });
1557
+ });
1558
+ } else {
1559
+ const targetStr = typeof config.target === "string" ? config.target : "model";
1560
+ scene.traverse((obj) => {
1561
+ if (obj instanceof THREE3.Mesh && obj.material instanceof THREE3.MeshPhongMaterial) {
1562
+ const isModel = obj.isSkinnedMesh || obj.skeleton;
1563
+ if (targetStr === "model" && isModel || targetStr === "all") {
1564
+ this.applyToMaterial(obj.material, targetStr);
1565
+ }
1566
+ }
1567
+ });
1568
+ }
1569
+ });
1570
+ console.log("[MultiFXAdapter] Layered effects applied successfully");
1571
+ }
1572
+ /**
1573
+ * 打印当前配置(调试用)
1574
+ */
1575
+ printConfig() {
1576
+ console.log("\n[MultiFXAdapter] Current Configuration:");
1577
+ console.log("\u2550".repeat(60));
1578
+ const summary = this.getSummary();
1579
+ console.log(`Total Effects: ${summary.totalFX}`);
1580
+ console.log(` - Scene-level (.x): ${summary.xFiles}`);
1581
+ console.log(` - Model-level (.fx): ${summary.fxFiles}`);
1582
+ console.log("\nLoad Order (by priority):");
1583
+ summary.configs.forEach((config, index) => {
1584
+ const icon = config.type === "x" ? "\u{1F30D}" : "\u{1F3A8}";
1585
+ console.log(`${index + 1}. ${icon} [${config.type.toUpperCase()}] ${config.description || config.path}`);
1586
+ console.log(` Priority: ${config.priority}, Target: ${config.target}`);
1587
+ if (config.features.length > 0) {
1588
+ console.log(` Features: ${config.features.join(", ")}`);
1589
+ }
1590
+ });
1591
+ console.log("\u2550".repeat(60) + "\n");
1592
+ }
1593
+ };
1594
+
51
1595
  // src/mmd/components/MMDPlayerBase.tsx
52
1596
  if (typeof window !== "undefined") {
53
- THREE.Cache.enabled = true;
1597
+ THREE3.Cache.enabled = true;
54
1598
  }
55
1599
  async function waitForMaterialsReady(object, renderer, scene, camera) {
56
1600
  const textures = [];
57
- let meshCount = 0;
58
1601
  object.traverse((obj) => {
59
- if (obj instanceof THREE.Mesh || obj instanceof THREE.SkinnedMesh) {
60
- meshCount++;
1602
+ if (obj instanceof THREE3.Mesh || obj instanceof THREE3.SkinnedMesh) {
61
1603
  const materials = Array.isArray(obj.material) ? obj.material : [obj.material];
62
1604
  materials.forEach((material) => {
63
- if (material instanceof THREE.Material) {
1605
+ if (material instanceof THREE3.Material) {
64
1606
  const textureProps = [
65
1607
  "map",
66
1608
  "lightMap",
@@ -82,7 +1624,7 @@ async function waitForMaterialsReady(object, renderer, scene, camera) {
82
1624
  ];
83
1625
  textureProps.forEach((prop) => {
84
1626
  const texture = material[prop];
85
- if (texture instanceof THREE.Texture && !textures.includes(texture)) {
1627
+ if (texture instanceof THREE3.Texture && !textures.includes(texture)) {
86
1628
  textures.push(texture);
87
1629
  }
88
1630
  });
@@ -90,22 +1632,18 @@ async function waitForMaterialsReady(object, renderer, scene, camera) {
90
1632
  });
91
1633
  }
92
1634
  });
93
- console.log(`[MMDPlayerBase] Found ${meshCount} meshes and ${textures.length} unique textures`);
94
1635
  const texturePromises = textures.map((texture, index) => {
95
1636
  return new Promise((resolve) => {
96
1637
  const image = texture.image;
97
1638
  if (!image) {
98
- console.log(`[MMDPlayerBase] Texture ${index + 1}/${textures.length}: No image`);
99
1639
  resolve();
100
1640
  return;
101
1641
  }
102
1642
  if (image instanceof HTMLImageElement) {
103
1643
  if (image.complete && image.naturalWidth > 0) {
104
- console.log(`[MMDPlayerBase] Texture ${index + 1}/${textures.length}: Already loaded`);
105
1644
  resolve();
106
1645
  } else {
107
1646
  const onLoad = () => {
108
- console.log(`[MMDPlayerBase] Texture ${index + 1}/${textures.length}: Loaded`);
109
1647
  image.removeEventListener("load", onLoad);
110
1648
  image.removeEventListener("error", onError);
111
1649
  resolve();
@@ -126,17 +1664,14 @@ async function waitForMaterialsReady(object, renderer, scene, camera) {
126
1664
  }, 5e3);
127
1665
  }
128
1666
  } else {
129
- console.log(`[MMDPlayerBase] Texture ${index + 1}/${textures.length}: Non-image type`);
130
1667
  resolve();
131
1668
  }
132
1669
  });
133
1670
  });
134
1671
  await Promise.all(texturePromises);
135
- console.log("[MMDPlayerBase] All texture images loaded");
136
1672
  textures.forEach((texture) => {
137
1673
  texture.needsUpdate = true;
138
1674
  });
139
- console.log("[MMDPlayerBase] Warming up renderer...");
140
1675
  for (let i = 0; i < 3; i++) {
141
1676
  await new Promise((resolve) => {
142
1677
  requestAnimationFrame(() => {
@@ -167,7 +1702,6 @@ async function waitForMaterialsReady(object, renderer, scene, camera) {
167
1702
  }
168
1703
  });
169
1704
  renderer.render(scene, camera);
170
- console.log(`[MMDPlayerBase] Warmup render ${i + 1}/3`);
171
1705
  } catch (renderError) {
172
1706
  console.warn("[MMDPlayerBase] Warmup render failed (shader error?), skipping...", renderError);
173
1707
  }
@@ -175,7 +1709,6 @@ async function waitForMaterialsReady(object, renderer, scene, camera) {
175
1709
  });
176
1710
  });
177
1711
  }
178
- console.log("[MMDPlayerBase] All materials and textures fully ready");
179
1712
  }
180
1713
  var MMDPlayerBase = forwardRef((props, ref) => {
181
1714
  const {
@@ -200,20 +1733,23 @@ var MMDPlayerBase = forwardRef((props, ref) => {
200
1733
  } = props;
201
1734
  const renderEffect = props.renderEffect || stage.renderEffect || "default";
202
1735
  const outlineOptions = { ...stage.outlineOptions, ...props.outlineOptions };
203
- const bloomOptions = { ...stage.bloomOptions, ...props.bloomOptions };
204
- const toonOptions = { ...stage.toonOptions, ...props.toonOptions };
1736
+ const fxPath = props.fxPath || stage.fxPath;
1737
+ const fxTexturePath = props.fxTexturePath || stage.fxTexturePath;
1738
+ const fxConfigs = props.fxConfigs || stage.fxConfigs;
205
1739
  const containerRef = useRef(null);
206
1740
  const sceneRef = useRef(null);
207
1741
  const cameraRef = useRef(null);
208
1742
  const rendererRef = useRef(null);
209
1743
  const outlineEffectRef = useRef(null);
210
- const composerRef = useRef(null);
211
1744
  const controlsRef = useRef(null);
212
1745
  const helperRef = useRef(null);
213
1746
  const axesHelperRef = useRef(null);
214
- const clockRef = useRef(new THREE.Clock());
1747
+ const clockRef = useRef(new THREE3.Clock());
215
1748
  const animationIdRef = useRef(null);
216
1749
  const resizeObserverRef = useRef(null);
1750
+ const fxEffectRef = useRef(null);
1751
+ const fxAdapterRef = useRef(null);
1752
+ const multiFXAdapterRef = useRef(null);
217
1753
  const isReadyRef = useRef(false);
218
1754
  const isPlayingRef = useRef(false);
219
1755
  const initIdRef = useRef(0);
@@ -222,7 +1758,7 @@ var MMDPlayerBase = forwardRef((props, ref) => {
222
1758
  const loopRef = useRef(loop);
223
1759
  const audioRef = useRef(null);
224
1760
  const audioListenerRef = useRef(null);
225
- const audioLoaderRef = useRef(new THREE.AudioLoader());
1761
+ const audioLoaderRef = useRef(new THREE3.AudioLoader());
226
1762
  const latestCallbacks = useRef({ onPlay, onPause, onEnded, onTimeUpdate });
227
1763
  useEffect(() => {
228
1764
  latestCallbacks.current = { onPlay, onPause, onEnded, onTimeUpdate };
@@ -313,23 +1849,15 @@ var MMDPlayerBase = forwardRef((props, ref) => {
313
1849
  if (modelSwitchCountRef.current === 0) {
314
1850
  startTimeRef.current = Date.now();
315
1851
  modelSwitchCountRef.current = 1;
316
- console.log("[MMDPlayerBase] \u{1F550} \u7CFB\u7EDF\u542F\u52A8\u65F6\u95F4:", new Date(startTimeRef.current).toLocaleString());
317
1852
  } else {
318
1853
  modelSwitchCountRef.current++;
319
- const runningTime = Date.now() - startTimeRef.current;
320
- const minutes = Math.floor(runningTime / 6e4);
321
- const seconds = Math.floor(runningTime % 6e4 / 1e3);
322
- console.log(`[MMDPlayerBase] \u{1F504} \u6A21\u578B\u5207\u6362 #${modelSwitchCountRef.current} (\u8FD0\u884C\u65F6\u95F4: ${minutes}\u5206${seconds}\u79D2)`);
323
1854
  }
324
1855
  try {
325
1856
  if (stage.enablePhysics !== false && !mobileOptimization.disablePhysics) {
326
- console.log("[MMDPlayerBase] Loading Ammo.js physics engine...");
327
1857
  await loadAmmo(stage.physicsPath);
328
1858
  if (checkCancelled()) return;
329
- console.log("[MMDPlayerBase] Ammo.js loaded successfully");
330
1859
  const Ammo = window.Ammo;
331
1860
  if (Ammo) {
332
- console.log("[MMDPlayerBase] Setting up physics component tracking...");
333
1861
  const originalBtDefaultCollisionConfiguration = Ammo.btDefaultCollisionConfiguration;
334
1862
  const originalBtCollisionDispatcher = Ammo.btCollisionDispatcher;
335
1863
  const originalBtDbvtBroadphase = Ammo.btDbvtBroadphase;
@@ -339,52 +1867,44 @@ var MMDPlayerBase = forwardRef((props, ref) => {
339
1867
  Ammo.btDefaultCollisionConfiguration = function(...args) {
340
1868
  const obj = new originalBtDefaultCollisionConfiguration(...args);
341
1869
  componentsRef.configs.push(obj);
342
- console.log(`[MMDPlayerBase] \u{1F50D} Captured btDefaultCollisionConfiguration #${componentsRef.configs.length}`);
343
1870
  return obj;
344
1871
  };
345
1872
  Ammo.btCollisionDispatcher = function(...args) {
346
1873
  const obj = new originalBtCollisionDispatcher(...args);
347
1874
  componentsRef.dispatchers.push(obj);
348
- console.log(`[MMDPlayerBase] \u{1F50D} Captured btCollisionDispatcher #${componentsRef.dispatchers.length}`);
349
1875
  return obj;
350
1876
  };
351
1877
  Ammo.btDbvtBroadphase = function(...args) {
352
1878
  const obj = new originalBtDbvtBroadphase(...args);
353
1879
  componentsRef.caches.push(obj);
354
- console.log(`[MMDPlayerBase] \u{1F50D} Captured btDbvtBroadphase #${componentsRef.caches.length}`);
355
1880
  return obj;
356
1881
  };
357
1882
  Ammo.btSequentialImpulseConstraintSolver = function(...args) {
358
1883
  const obj = new originalBtSequentialImpulseConstraintSolver(...args);
359
1884
  componentsRef.solvers.push(obj);
360
- console.log(`[MMDPlayerBase] \u{1F50D} Captured btSequentialImpulseConstraintSolver #${componentsRef.solvers.length}`);
361
1885
  return obj;
362
1886
  };
363
1887
  Ammo.btDiscreteDynamicsWorld = function(...args) {
364
1888
  const obj = new originalBtDiscreteDynamicsWorld(...args);
365
1889
  componentsRef.worlds.push(obj);
366
- console.log(`[MMDPlayerBase] \u{1F50D} Captured btDiscreteDynamicsWorld #${componentsRef.worlds.length}`);
367
1890
  return obj;
368
1891
  };
369
- console.log("[MMDPlayerBase] \u2705 Physics component tracking setup complete");
370
1892
  }
371
- } else {
372
- console.log("[MMDPlayerBase] Physics disabled");
373
1893
  }
374
1894
  const container = containerRef.current;
375
1895
  const width = container.clientWidth || 300;
376
1896
  const height = container.clientHeight || 150;
377
- const scene = new THREE.Scene();
1897
+ const scene = new THREE3.Scene();
378
1898
  if (stage.backgroundColor && stage.backgroundColor !== "transparent") {
379
- scene.background = new THREE.Color(stage.backgroundColor);
1899
+ scene.background = new THREE3.Color(stage.backgroundColor);
380
1900
  } else if (stage.backgroundImage) {
381
- const textureLoader = new THREE.TextureLoader();
1901
+ const textureLoader = new THREE3.TextureLoader();
382
1902
  textureLoader.load(stage.backgroundImage, (texture) => {
383
1903
  scene.background = texture;
384
1904
  });
385
1905
  }
386
1906
  sceneRef.current = scene;
387
- const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 2e3);
1907
+ const camera = new THREE3.PerspectiveCamera(45, width / height, 0.1, 2e3);
388
1908
  if (stage.cameraPosition) {
389
1909
  const pos = stage.cameraPosition;
390
1910
  camera.position.set(pos.x, pos.y, pos.z);
@@ -392,10 +1912,10 @@ var MMDPlayerBase = forwardRef((props, ref) => {
392
1912
  camera.position.set(0, 20, 30);
393
1913
  }
394
1914
  cameraRef.current = camera;
395
- const listener = new THREE.AudioListener();
1915
+ const listener = new THREE3.AudioListener();
396
1916
  camera.add(listener);
397
1917
  audioListenerRef.current = listener;
398
- const renderer = new THREE.WebGLRenderer({
1918
+ const renderer = new THREE3.WebGLRenderer({
399
1919
  antialias: !mobileOptimization.enabled,
400
1920
  alpha: true,
401
1921
  preserveDrawingBuffer: true
@@ -403,12 +1923,7 @@ var MMDPlayerBase = forwardRef((props, ref) => {
403
1923
  renderer.setSize(width, height);
404
1924
  const pixelRatio = mobileOptimization.enabled ? mobileOptimization.pixelRatio || Math.min(window.devicePixelRatio, 2) : window.devicePixelRatio;
405
1925
  renderer.setPixelRatio(pixelRatio);
406
- console.log("[MMDPlayerBase] Pixel ratio set to:", pixelRatio);
407
- if (renderEffect.includes("outline") || toonOptions.enabled) {
408
- renderer.toneMapping = THREE.NoToneMapping;
409
- } else {
410
- renderer.toneMapping = THREE.ACESFilmicToneMapping;
411
- }
1926
+ renderer.toneMapping = THREE3.ACESFilmicToneMapping;
412
1927
  if (checkCancelled()) {
413
1928
  renderer.dispose();
414
1929
  return;
@@ -421,31 +1936,20 @@ var MMDPlayerBase = forwardRef((props, ref) => {
421
1936
  renderer.domElement.style.position = "relative";
422
1937
  if (stage.enableShadow !== false && !mobileOptimization.reduceShadowQuality) {
423
1938
  renderer.shadowMap.enabled = true;
424
- renderer.shadowMap.type = THREE.PCFSoftShadowMap;
1939
+ renderer.shadowMap.type = THREE3.PCFSoftShadowMap;
425
1940
  }
426
1941
  container.appendChild(renderer.domElement);
427
1942
  rendererRef.current = renderer;
428
1943
  const effect = new OutlineEffect(renderer, {
429
1944
  defaultThickness: outlineOptions.thickness ?? 3e-3,
430
- defaultColor: new THREE.Color(outlineOptions.color ?? "#000000").toArray(),
1945
+ defaultColor: new THREE3.Color(outlineOptions.color ?? "#000000").toArray(),
431
1946
  defaultAlpha: 1,
432
1947
  defaultKeepAlive: true
433
1948
  });
434
1949
  outlineEffectRef.current = effect;
435
- const composer = new EffectComposer(renderer);
436
- const renderPass = new RenderPass(scene, camera);
437
- composer.addPass(renderPass);
438
- const bloomPass = new UnrealBloomPass(
439
- new THREE.Vector2(width, height),
440
- bloomOptions.strength ?? 1,
441
- bloomOptions.radius ?? 0.4,
442
- bloomOptions.threshold ?? 0.8
443
- );
444
- composer.addPass(bloomPass);
445
- composerRef.current = composer;
446
- const ambientLight = new THREE.AmbientLight(16777215, stage.ambientLightIntensity ?? 0.5);
1950
+ const ambientLight = new THREE3.AmbientLight(16777215, stage.ambientLightIntensity ?? 0.5);
447
1951
  scene.add(ambientLight);
448
- const dirLight = new THREE.DirectionalLight(16777215, stage.directionalLightIntensity ?? 0.8);
1952
+ const dirLight = new THREE3.DirectionalLight(16777215, stage.directionalLightIntensity ?? 0.8);
449
1953
  dirLight.position.set(0, 10, 0);
450
1954
  if (stage.enableShadow !== false) {
451
1955
  dirLight.castShadow = true;
@@ -454,6 +1958,62 @@ var MMDPlayerBase = forwardRef((props, ref) => {
454
1958
  dirLight.shadow.bias = -1e-4;
455
1959
  }
456
1960
  scene.add(dirLight);
1961
+ if (fxConfigs && fxConfigs.length > 0) {
1962
+ try {
1963
+ console.log("[MMDPlayerBase] Loading multiple FX files:", fxConfigs.length);
1964
+ const multiFX = new MultiFXAdapter({
1965
+ mergeStrategy: "override",
1966
+ autoLoadTextures: true
1967
+ });
1968
+ await multiFX.addMultipleFX(fxConfigs);
1969
+ multiFXAdapterRef.current = multiFX;
1970
+ multiFX.printConfig();
1971
+ multiFX.applyToScene(scene, renderer);
1972
+ console.log("[MMDPlayerBase] Multiple FX files loaded successfully");
1973
+ } catch (error) {
1974
+ console.error("[MMDPlayerBase] Failed to load multiple FX files:", error);
1975
+ }
1976
+ } else if (fxPath) {
1977
+ try {
1978
+ console.log("[MMDPlayerBase] Loading single FX file:", fxPath);
1979
+ const parser = new FXParser();
1980
+ const fxEffect = await parser.loadAndParse(fxPath);
1981
+ fxEffectRef.current = fxEffect;
1982
+ const fxAdapter = new FXToThreeAdapter(fxEffect, fxTexturePath || "");
1983
+ fxAdapterRef.current = fxAdapter;
1984
+ console.log("[MMDPlayerBase] Loading FX textures...");
1985
+ await fxAdapter.loadTextures();
1986
+ console.log("[MMDPlayerBase] Applying FX render config (renderer only)...");
1987
+ const renderConfig = fxAdapter.extractRenderConfig();
1988
+ if (renderConfig.toneMapping !== void 0) {
1989
+ renderer.toneMapping = renderConfig.toneMapping;
1990
+ console.log("[MMDPlayerBase] Applied toneMapping from FX:", renderConfig.toneMapping === THREE3.NoToneMapping ? "NoToneMapping" : "ACESFilmicToneMapping");
1991
+ }
1992
+ if (renderConfig.toneMappingExposure !== void 0) {
1993
+ renderer.toneMappingExposure = renderConfig.toneMappingExposure;
1994
+ }
1995
+ if (renderConfig.enableShadow !== void 0) {
1996
+ renderer.shadowMap.enabled = renderConfig.enableShadow;
1997
+ if (renderConfig.enableShadow) {
1998
+ renderer.shadowMap.type = THREE3.PCFSoftShadowMap;
1999
+ }
2000
+ }
2001
+ if (renderConfig.ambientLightIntensity !== void 0) {
2002
+ ambientLight.intensity = renderConfig.ambientLightIntensity;
2003
+ console.log("[MMDPlayerBase] Updated ambient light from FX:", renderConfig.ambientLightIntensity);
2004
+ }
2005
+ if (renderConfig.directionalLightIntensity !== void 0) {
2006
+ dirLight.intensity = renderConfig.directionalLightIntensity;
2007
+ console.log("[MMDPlayerBase] Updated directional light from FX:", renderConfig.directionalLightIntensity);
2008
+ }
2009
+ if (renderConfig.lightDirection) {
2010
+ dirLight.position.copy(renderConfig.lightDirection).multiplyScalar(10);
2011
+ }
2012
+ console.log("[MMDPlayerBase] FX file loaded successfully");
2013
+ } catch (error) {
2014
+ console.error("[MMDPlayerBase] Failed to load FX file:", error);
2015
+ }
2016
+ }
457
2017
  const controls = new OrbitControls(camera, renderer.domElement);
458
2018
  controls.minDistance = 10;
459
2019
  controls.maxDistance = 100;
@@ -469,7 +2029,7 @@ var MMDPlayerBase = forwardRef((props, ref) => {
469
2029
  });
470
2030
  controlsRef.current = controls;
471
2031
  if (showAxes) {
472
- const axesHelper = new THREE.AxesHelper(20);
2032
+ const axesHelper = new THREE3.AxesHelper(20);
473
2033
  scene.add(axesHelper);
474
2034
  axesHelperRef.current = axesHelper;
475
2035
  }
@@ -481,21 +2041,12 @@ var MMDPlayerBase = forwardRef((props, ref) => {
481
2041
  cameraRef.current.aspect = w / h;
482
2042
  cameraRef.current.updateProjectionMatrix();
483
2043
  rendererRef.current.setSize(w, h);
484
- if (composerRef.current) {
485
- composerRef.current.setSize(w, h);
486
- }
487
2044
  };
488
2045
  const resizeObserver = new ResizeObserver(onResize);
489
2046
  resizeObserver.observe(container);
490
2047
  resizeObserverRef.current = resizeObserver;
491
2048
  onResize();
492
- console.log("[MMDPlayerBase] Starting render loop (animation paused)");
493
2049
  animate();
494
- console.log("[MMDPlayerBase] Start loading resources...", {
495
- model: resources.modelPath,
496
- stage: resources.stageModelPath,
497
- motion: resources.motionPath
498
- });
499
2050
  const loader = new MMDLoader();
500
2051
  const helper = new MMDAnimationHelper({
501
2052
  afterglow: 2
@@ -503,7 +2054,6 @@ var MMDPlayerBase = forwardRef((props, ref) => {
503
2054
  helperRef.current = helper;
504
2055
  const loadModelPromise = new Promise((resolve, reject) => {
505
2056
  if (resources.motionPath) {
506
- console.log("[MMDPlayerBase] Loading model with motion:", resources.motionPath);
507
2057
  loader.loadWithAnimation(
508
2058
  resources.modelPath,
509
2059
  resources.motionPath,
@@ -519,7 +2069,6 @@ var MMDPlayerBase = forwardRef((props, ref) => {
519
2069
  (err) => reject(err)
520
2070
  );
521
2071
  } else {
522
- console.log("[MMDPlayerBase] Loading model only");
523
2072
  loader.load(
524
2073
  resources.modelPath,
525
2074
  (mesh2) => {
@@ -537,26 +2086,21 @@ var MMDPlayerBase = forwardRef((props, ref) => {
537
2086
  });
538
2087
  const { mesh, animation } = await loadModelPromise;
539
2088
  if (checkCancelled()) return;
540
- console.log("[MMDPlayerBase] Model loaded:", mesh);
541
2089
  if (animation) {
542
2090
  animationClipRef.current = animation;
543
2091
  durationRef.current = animation.duration;
544
- console.log("[MMDPlayerBase] Animation duration:", animation.duration);
545
2092
  }
546
2093
  mesh.castShadow = true;
547
2094
  mesh.receiveShadow = true;
548
- console.log("[MMDPlayerBase] Waiting for all materials and textures to load...");
549
- const tempScene = new THREE.Scene();
2095
+ const tempScene = new THREE3.Scene();
550
2096
  tempScene.add(mesh);
551
2097
  await waitForMaterialsReady(mesh, renderer, tempScene, camera);
552
2098
  if (checkCancelled()) return;
553
- console.log("[MMDPlayerBase] \u2705 All materials and textures loaded");
554
2099
  tempScene.remove(mesh);
555
- const box = new THREE.Box3().setFromObject(mesh);
2100
+ const box = new THREE3.Box3().setFromObject(mesh);
556
2101
  if (!box.isEmpty()) {
557
- const center = box.getCenter(new THREE.Vector3());
558
- const size = box.getSize(new THREE.Vector3());
559
- console.log("[MMDPlayerBase] Model bounds:", { center, size });
2102
+ const center = box.getCenter(new THREE3.Vector3());
2103
+ const size = box.getSize(new THREE3.Vector3());
560
2104
  if (!stage.cameraTarget) {
561
2105
  controls.target.set(center.x, center.y + size.y * 0.35, center.z);
562
2106
  if (!stage.cameraPosition) {
@@ -570,21 +2114,23 @@ var MMDPlayerBase = forwardRef((props, ref) => {
570
2114
  center.z + dist
571
2115
  // Z: 在模型正前方(+Z 方向)
572
2116
  );
573
- console.log("[MMDPlayerBase] Auto camera position:", camera.position);
574
2117
  }
575
2118
  controls.update();
576
2119
  }
577
2120
  }
578
2121
  const enablePhysics = stage.enablePhysics !== false && !mobileOptimization.disablePhysics;
2122
+ console.log("[MMDPlayerBase] \u{1F3A8} Traversing model mesh to apply FX and outline, multiFX:", !!multiFXAdapterRef.current, "singleFX:", !!fxAdapterRef.current);
2123
+ let materialCount = 0;
579
2124
  mesh.traverse((obj) => {
580
- if (obj instanceof THREE.Mesh || obj instanceof THREE.SkinnedMesh) {
2125
+ if (obj instanceof THREE3.Mesh || obj instanceof THREE3.SkinnedMesh) {
581
2126
  const materials = Array.isArray(obj.material) ? obj.material : [obj.material];
2127
+ materialCount += materials.length;
582
2128
  materials.forEach((m) => {
583
2129
  if (!m.userData) m.userData = {};
584
2130
  if (!m.userData.outlineParameters) {
585
2131
  m.userData.outlineParameters = {
586
2132
  thickness: outlineOptions.thickness ?? 3e-3,
587
- color: new THREE.Color(outlineOptions.color ?? "#000000").toArray(),
2133
+ color: new THREE3.Color(outlineOptions.color ?? "#000000").toArray(),
588
2134
  alpha: 1,
589
2135
  visible: true,
590
2136
  keepAlive: true
@@ -594,46 +2140,74 @@ var MMDPlayerBase = forwardRef((props, ref) => {
594
2140
  m.userData.outlineParameters.thickness = outlineOptions.thickness;
595
2141
  }
596
2142
  if (outlineOptions.color !== void 0) {
597
- m.userData.outlineParameters.color = new THREE.Color(outlineOptions.color).toArray();
2143
+ m.userData.outlineParameters.color = new THREE3.Color(outlineOptions.color).toArray();
598
2144
  }
599
2145
  }
600
- if (m instanceof THREE.MeshPhongMaterial) {
601
- if (toonOptions.enabled !== false && (toonOptions.enabled || renderEffect.includes("outline"))) {
602
- m.shininess = toonOptions.shininess ?? 0;
603
- m.specular.setScalar(0);
604
- if (toonOptions.forceHardShading && m.toonMap) {
605
- m.toonMap.magFilter = THREE.NearestFilter;
606
- m.toonMap.minFilter = THREE.NearestFilter;
607
- m.toonMap.needsUpdate = true;
2146
+ if (m instanceof THREE3.MeshPhongMaterial || m instanceof THREE3.MeshToonMaterial) {
2147
+ console.log("[MMDPlayerBase] \u{1F3A8} Applying FX to model material (type:", m.type, "), multiFX:", !!multiFXAdapterRef.current, "singleFX:", !!fxAdapterRef.current);
2148
+ if (multiFXAdapterRef.current) {
2149
+ console.log("[MMDPlayerBase] Using MultiFXAdapter");
2150
+ multiFXAdapterRef.current.applyToMaterial(m, "model");
2151
+ } else if (fxAdapterRef.current) {
2152
+ console.log("[MMDPlayerBase] Using single FXAdapter");
2153
+ const materialConfig = fxAdapterRef.current.extractMaterialConfig();
2154
+ console.log("[MMDPlayerBase] Extracted material config:");
2155
+ console.log(" - color:", materialConfig.color);
2156
+ console.log(" - emissive:", materialConfig.emissive);
2157
+ console.log(" - specular:", materialConfig.specular);
2158
+ console.log(" - shininess:", materialConfig.shininess);
2159
+ if (materialConfig.color) {
2160
+ const isBlack = materialConfig.color.r === 0 && materialConfig.color.g === 0 && materialConfig.color.b === 0;
2161
+ if (!isBlack) {
2162
+ m.color.copy(materialConfig.color);
2163
+ console.log("[MMDPlayerBase] Applied color:", materialConfig.color);
2164
+ } else {
2165
+ console.log("[MMDPlayerBase] Skipping black color (0,0,0) to preserve original material");
2166
+ }
2167
+ }
2168
+ if (materialConfig.emissive) {
2169
+ const isBlack = materialConfig.emissive.r === 0 && materialConfig.emissive.g === 0 && materialConfig.emissive.b === 0;
2170
+ if (!isBlack) {
2171
+ m.emissive.copy(materialConfig.emissive);
2172
+ console.log("[MMDPlayerBase] Applied emissive:", materialConfig.emissive);
2173
+ }
2174
+ }
2175
+ if (materialConfig.specular && m.specular) {
2176
+ m.specular.copy(materialConfig.specular);
2177
+ console.log("[MMDPlayerBase] Applied specular:", materialConfig.specular);
2178
+ }
2179
+ if (materialConfig.shininess !== void 0 && m.shininess !== void 0) {
2180
+ console.log("[MMDPlayerBase] Applying shininess:", materialConfig.shininess);
2181
+ m.shininess = materialConfig.shininess;
608
2182
  }
609
2183
  }
2184
+ } else {
2185
+ console.log("[MMDPlayerBase] Material type not supported for FX:", m.type);
610
2186
  }
611
2187
  });
612
2188
  }
613
2189
  });
2190
+ console.log("[MMDPlayerBase] \u{1F3A8} Model traverse complete, processed materials:", materialCount);
614
2191
  helper.add(mesh, {
615
2192
  animation,
616
2193
  physics: enablePhysics
617
2194
  });
618
2195
  scene.add(mesh);
619
- console.log("[MMDPlayerBase] \u2705 Model added to scene (fully loaded)");
620
2196
  const isMobileDevice = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || window.innerWidth <= 768;
621
2197
  if (isMobileDevice) {
622
2198
  console.log("[MMDPlayerBase] \u{1F4F1} Mobile device detected, applying optimizations...");
623
- if (renderer.capabilities.vertexTextures) {
624
- console.log("[MMDPlayerBase] \u2705 Vertex textures supported");
625
- } else {
2199
+ if (!renderer.capabilities.vertexTextures) {
626
2200
  console.log("[MMDPlayerBase] \u26A0\uFE0F Vertex textures NOT supported");
627
2201
  }
628
2202
  let simplifiedMaterialCount = 0;
629
2203
  mesh.traverse((child) => {
630
- if (child instanceof THREE.Mesh || child instanceof THREE.SkinnedMesh) {
2204
+ if (child instanceof THREE3.Mesh || child instanceof THREE3.SkinnedMesh) {
631
2205
  const materials = Array.isArray(child.material) ? child.material : [child.material];
632
2206
  materials.forEach((material, idx) => {
633
- if (material instanceof THREE.MeshPhongMaterial || material instanceof THREE.MeshStandardMaterial) {
2207
+ if (material instanceof THREE3.MeshPhongMaterial || material instanceof THREE3.MeshStandardMaterial) {
634
2208
  const originalColor = material.color?.clone();
635
2209
  const originalMap = material.map;
636
- const basicMaterial = new THREE.MeshBasicMaterial({
2210
+ const basicMaterial = new THREE3.MeshBasicMaterial({
637
2211
  color: originalColor || 16777215,
638
2212
  map: originalMap,
639
2213
  transparent: material.transparent,
@@ -652,20 +2226,14 @@ var MMDPlayerBase = forwardRef((props, ref) => {
652
2226
  });
653
2227
  }
654
2228
  });
655
- if (simplifiedMaterialCount > 0) {
656
- console.log(`[MMDPlayerBase] \u2705 Simplified ${simplifiedMaterialCount} materials to MeshBasicMaterial`);
657
- }
658
2229
  const MAX_BONES = 64;
659
2230
  if (mesh.skeleton) {
660
2231
  const boneCount = mesh.skeleton.bones.length;
661
2232
  if (boneCount > MAX_BONES) {
662
2233
  console.warn(`[MMDPlayerBase] \u26A0\uFE0F Model has ${boneCount} bones (max recommended: ${MAX_BONES})`);
663
2234
  console.warn(`[MMDPlayerBase] This may cause performance issues on mobile devices`);
664
- } else {
665
- console.log(`[MMDPlayerBase] \u2705 Bone count: ${boneCount} (within limit)`);
666
2235
  }
667
2236
  }
668
- console.log("[MMDPlayerBase] \u{1F4F1} Mobile optimizations applied");
669
2237
  }
670
2238
  if (resources.cameraPath) {
671
2239
  loader.loadAnimation(
@@ -684,7 +2252,6 @@ var MMDPlayerBase = forwardRef((props, ref) => {
684
2252
  const stagePaths = Array.isArray(resources.stageModelPath) ? resources.stageModelPath : resources.stageModelPath ? [resources.stageModelPath] : [];
685
2253
  for (const stagePath of stagePaths) {
686
2254
  try {
687
- console.log(`[MMDPlayerBase] Loading stage from: ${stagePath}`);
688
2255
  const stageMesh = await new Promise((resolve, reject) => {
689
2256
  loader.load(
690
2257
  stagePath,
@@ -700,12 +2267,15 @@ var MMDPlayerBase = forwardRef((props, ref) => {
700
2267
  });
701
2268
  if (checkCancelled()) return;
702
2269
  console.log(`[MMDPlayerBase] Stage model loaded: ${stagePath}`, stageMesh);
2270
+ console.log("[MMDPlayerBase] \u{1F3A8} Traversing stage mesh to apply FX, multiFX:", !!multiFXAdapterRef.current, "singleFX:", !!fxAdapterRef.current);
2271
+ let stageMaterialCount = 0;
703
2272
  stageMesh.traverse((child) => {
704
- if (child instanceof THREE.Mesh) {
2273
+ if (child instanceof THREE3.Mesh) {
705
2274
  child.castShadow = true;
706
2275
  child.receiveShadow = true;
707
2276
  const mesh2 = child;
708
2277
  const materials = Array.isArray(mesh2.material) ? mesh2.material : [mesh2.material];
2278
+ stageMaterialCount += materials.length;
709
2279
  materials.forEach((m, idx) => {
710
2280
  if (m.morphTargets) {
711
2281
  m.morphTargets = false;
@@ -722,20 +2292,56 @@ var MMDPlayerBase = forwardRef((props, ref) => {
722
2292
  if (!m.userData) m.userData = {};
723
2293
  m.userData.outlineParameters = {
724
2294
  thickness: outlineOptions.thickness ?? 3e-3,
725
- color: new THREE.Color(outlineOptions.color ?? "#000000").toArray(),
2295
+ color: new THREE3.Color(outlineOptions.color ?? "#000000").toArray(),
726
2296
  alpha: 1,
727
2297
  visible: true,
728
2298
  keepAlive: true
729
2299
  };
730
- if (m instanceof THREE.MeshPhongMaterial) {
731
- if (toonOptions.enabled !== false && (toonOptions.enabled || renderEffect.includes("outline"))) {
732
- m.shininess = toonOptions.shininess ?? 0;
733
- m.specular.setScalar(0);
2300
+ if (m instanceof THREE3.MeshPhongMaterial || m instanceof THREE3.MeshToonMaterial) {
2301
+ console.log("[MMDPlayerBase] \u{1F3A8} Applying FX to stage material (type:", m.type, "), multiFX:", !!multiFXAdapterRef.current, "singleFX:", !!fxAdapterRef.current);
2302
+ if (multiFXAdapterRef.current) {
2303
+ console.log("[MMDPlayerBase] Using MultiFXAdapter for stage");
2304
+ multiFXAdapterRef.current.applyToMaterial(m, "stage");
2305
+ } else if (fxAdapterRef.current) {
2306
+ console.log("[MMDPlayerBase] Using single FXAdapter for stage");
2307
+ const materialConfig = fxAdapterRef.current.extractMaterialConfig();
2308
+ console.log("[MMDPlayerBase] Extracted material config for stage:");
2309
+ console.log(" - color:", materialConfig.color);
2310
+ console.log(" - emissive:", materialConfig.emissive);
2311
+ console.log(" - specular:", materialConfig.specular);
2312
+ console.log(" - shininess:", materialConfig.shininess);
2313
+ if (materialConfig.color) {
2314
+ const isBlack = materialConfig.color.r === 0 && materialConfig.color.g === 0 && materialConfig.color.b === 0;
2315
+ if (!isBlack) {
2316
+ m.color.copy(materialConfig.color);
2317
+ console.log("[MMDPlayerBase] Applied color to stage:", materialConfig.color);
2318
+ } else {
2319
+ console.log("[MMDPlayerBase] Skipping black color (0,0,0) for stage to preserve original material");
2320
+ }
2321
+ }
2322
+ if (materialConfig.emissive) {
2323
+ const isBlack = materialConfig.emissive.r === 0 && materialConfig.emissive.g === 0 && materialConfig.emissive.b === 0;
2324
+ if (!isBlack) {
2325
+ m.emissive.copy(materialConfig.emissive);
2326
+ console.log("[MMDPlayerBase] Applied emissive to stage:", materialConfig.emissive);
2327
+ }
2328
+ }
2329
+ if (materialConfig.specular && m.specular) {
2330
+ m.specular.copy(materialConfig.specular);
2331
+ console.log("[MMDPlayerBase] Applied specular to stage:", materialConfig.specular);
2332
+ }
2333
+ if (materialConfig.shininess !== void 0 && m.shininess !== void 0) {
2334
+ console.log("[MMDPlayerBase] Applying shininess to stage:", materialConfig.shininess);
2335
+ m.shininess = materialConfig.shininess;
2336
+ }
734
2337
  }
2338
+ } else {
2339
+ console.log("[MMDPlayerBase] Stage material type not supported for FX:", m.type);
735
2340
  }
736
2341
  });
737
2342
  }
738
2343
  });
2344
+ console.log("[MMDPlayerBase] \u{1F3A8} Stage traverse complete, processed materials:", stageMaterialCount);
739
2345
  try {
740
2346
  await waitForMaterialsReady(stageMesh, renderer, scene, camera);
741
2347
  } catch (e) {
@@ -743,14 +2349,13 @@ var MMDPlayerBase = forwardRef((props, ref) => {
743
2349
  }
744
2350
  if (checkCancelled()) return;
745
2351
  scene.add(stageMesh);
746
- const stageBox = new THREE.Box3().setFromObject(stageMesh);
747
- const stageSize = stageBox.getSize(new THREE.Vector3());
2352
+ const stageBox = new THREE3.Box3().setFromObject(stageMesh);
2353
+ const stageSize = stageBox.getSize(new THREE3.Vector3());
748
2354
  if (stageSize.length() < 1) {
749
2355
  stageMesh.scale.multiplyScalar(100);
750
2356
  } else if (stageSize.y < 5) {
751
2357
  stageMesh.scale.multiplyScalar(10);
752
2358
  }
753
- console.log(`[MMDPlayerBase] \u2705 Stage added: ${stagePath}`);
754
2359
  if (resources.stageMotionPath) {
755
2360
  loader.loadAnimation(resources.stageMotionPath, stageMesh, (anim) => {
756
2361
  if (!checkCancelled()) helper.add(stageMesh, { animation: anim });
@@ -762,21 +2367,10 @@ var MMDPlayerBase = forwardRef((props, ref) => {
762
2367
  }
763
2368
  if (checkCancelled()) return;
764
2369
  isReadyRef.current = true;
765
- console.log("[MMDPlayerBase] \u{1F389} All resources fully loaded and ready!");
766
- console.log("[MMDPlayerBase] \u{1F4CA} Summary:");
767
- console.log(`[MMDPlayerBase] - Model: \u2705 Fully loaded with all textures`);
768
- if (resources.stageModelPath) {
769
- console.log(`[MMDPlayerBase] - Stage: \u2705 Fully loaded with all textures`);
770
- }
771
- if (animation) {
772
- console.log(`[MMDPlayerBase] - Animation: \u2705 Ready (${animation.duration.toFixed(2)}s)`);
773
- }
774
- console.log("[MMDPlayerBase] \u{1F514} Triggering onLoad callback");
775
2370
  onLoad?.();
776
2371
  if (autoPlay) {
777
2372
  setTimeout(() => {
778
2373
  if (checkCancelled()) return;
779
- console.log("[MMDPlayerBase] \u{1F3AC} Starting animation playback (after materials fully loaded)");
780
2374
  isPlayingRef.current = true;
781
2375
  if (!clockRef.current.running) clockRef.current.start();
782
2376
  onPlay?.();
@@ -849,47 +2443,24 @@ ${errorMessage}
849
2443
  console.log("[MMDPlayerBase] Found meshes count:", meshes.length);
850
2444
  if (meshes && Array.isArray(meshes) && meshes.length > 0) {
851
2445
  meshes.forEach((mesh, idx) => {
852
- console.log(`[MMDPlayerBase] Cleaning mesh ${idx}:`, mesh.uuid);
853
2446
  let meshData = null;
854
2447
  if (helperObjects instanceof WeakMap) {
855
- console.log("[MMDPlayerBase] Accessing WeakMap with mesh as key...");
856
2448
  meshData = helperObjects.get(mesh);
857
- if (meshData) {
858
- const meshDataKeys = Object.keys(meshData);
859
- console.log(`[MMDPlayerBase] \u2705 Got meshData from WeakMap, keys (${meshDataKeys.length}):`, meshDataKeys);
860
- const physicsRelatedKeys = meshDataKeys.filter((k) => k.toLowerCase().includes("phys"));
861
- if (physicsRelatedKeys.length > 0) {
862
- console.log(`[MMDPlayerBase] Physics-related keys:`, physicsRelatedKeys);
863
- physicsRelatedKeys.forEach((key) => {
864
- const value = meshData[key];
865
- console.log(`[MMDPlayerBase] ${key}:`, typeof value, value?.constructor?.name || value);
866
- });
867
- }
868
- } else {
869
- console.log("[MMDPlayerBase] \u26A0\uFE0F No meshData found in WeakMap for this mesh");
870
- }
871
2449
  }
872
2450
  if (!meshData) {
873
- console.log("[MMDPlayerBase] Using mesh itself as meshData");
874
2451
  meshData = mesh;
875
2452
  }
876
2453
  const physics = meshData?.physics;
877
2454
  if (physics) {
878
2455
  try {
879
- console.log("[MMDPlayerBase] \u{1F3AF} Starting physics cleanup for mesh", idx);
880
- console.log("[MMDPlayerBase] Debug: physics object keys:", Object.keys(physics));
881
2456
  if (typeof physics.dispose === "function") {
882
- console.log("[MMDPlayerBase] Calling MMDPhysics.dispose()...");
883
2457
  physics.dispose();
884
- console.log("[MMDPlayerBase] \u2705 MMDPhysics.dispose() completed");
885
2458
  } else {
886
- console.log("[MMDPlayerBase] No dispose method, manually cleaning physics components...");
887
2459
  const Ammo2 = window.Ammo;
888
2460
  if (!Ammo2 || !Ammo2.destroy) {
889
2461
  console.warn("[MMDPlayerBase] \u26A0\uFE0F Ammo.destroy not available");
890
2462
  } else {
891
2463
  if (physics.world && Array.isArray(physics.bodies) && physics.bodies.length > 0) {
892
- console.log(`[MMDPlayerBase] Cleaning ${physics.bodies.length} rigid bodies...`);
893
2464
  for (let i = physics.bodies.length - 1; i >= 0; i--) {
894
2465
  try {
895
2466
  const body = physics.bodies[i];
@@ -901,10 +2472,8 @@ ${errorMessage}
901
2472
  }
902
2473
  }
903
2474
  physics.bodies.length = 0;
904
- console.log("[MMDPlayerBase] \u2705 All rigid bodies removed");
905
2475
  }
906
2476
  if (physics.world && Array.isArray(physics.constraints) && physics.constraints.length > 0) {
907
- console.log(`[MMDPlayerBase] Cleaning ${physics.constraints.length} constraints...`);
908
2477
  for (let i = physics.constraints.length - 1; i >= 0; i--) {
909
2478
  try {
910
2479
  const constraint = physics.constraints[i];
@@ -916,18 +2485,14 @@ ${errorMessage}
916
2485
  }
917
2486
  }
918
2487
  physics.constraints.length = 0;
919
- console.log("[MMDPlayerBase] \u2705 All constraints removed");
920
2488
  }
921
2489
  }
922
2490
  }
923
2491
  meshData.physics = null;
924
- console.log("[MMDPlayerBase] \u2705 Physics cleanup completed for mesh", idx);
925
2492
  } catch (physicsError) {
926
2493
  console.error("[MMDPlayerBase] \u274C Error cleaning up physics:", physicsError);
927
2494
  console.error("[MMDPlayerBase] Physics error stack:", physicsError.stack);
928
2495
  }
929
- } else {
930
- console.log("[MMDPlayerBase] \u26A0\uFE0F No physics object found for mesh", idx);
931
2496
  }
932
2497
  if (meshData?.mixer) {
933
2498
  meshData.mixer.stopAllAction();
@@ -955,19 +2520,10 @@ ${errorMessage}
955
2520
  });
956
2521
  meshes.length = 0;
957
2522
  }
958
- console.log("[MMDPlayerBase] \u{1F525} Starting CRITICAL physics components cleanup...");
959
2523
  const Ammo = window.Ammo;
960
2524
  if (Ammo && Ammo.destroy) {
961
2525
  const components = physicsComponentsRef.current;
962
- console.log(`[MMDPlayerBase] \u{1F4CA} Physics components count:`, {
963
- worlds: components.worlds.length,
964
- solvers: components.solvers.length,
965
- caches: components.caches.length,
966
- dispatchers: components.dispatchers.length,
967
- configs: components.configs.length
968
- });
969
2526
  if (components.worlds.length > 0) {
970
- console.log(`[MMDPlayerBase] \u{1F5D1}\uFE0F Destroying ${components.worlds.length} btDiscreteDynamicsWorld(s)...`);
971
2527
  for (let i = components.worlds.length - 1; i >= 0; i--) {
972
2528
  try {
973
2529
  Ammo.destroy(components.worlds[i]);
@@ -976,10 +2532,8 @@ ${errorMessage}
976
2532
  }
977
2533
  }
978
2534
  components.worlds.length = 0;
979
- console.log("[MMDPlayerBase] \u2705 All btDiscreteDynamicsWorld destroyed");
980
2535
  }
981
2536
  if (components.solvers.length > 0) {
982
- console.log(`[MMDPlayerBase] \u{1F5D1}\uFE0F Destroying ${components.solvers.length} btSequentialImpulseConstraintSolver(s)...`);
983
2537
  for (let i = components.solvers.length - 1; i >= 0; i--) {
984
2538
  try {
985
2539
  Ammo.destroy(components.solvers[i]);
@@ -988,10 +2542,8 @@ ${errorMessage}
988
2542
  }
989
2543
  }
990
2544
  components.solvers.length = 0;
991
- console.log("[MMDPlayerBase] \u2705 All btSequentialImpulseConstraintSolver destroyed");
992
2545
  }
993
2546
  if (components.caches.length > 0) {
994
- console.log(`[MMDPlayerBase] \u{1F5D1}\uFE0F Destroying ${components.caches.length} btDbvtBroadphase(s)...`);
995
2547
  for (let i = components.caches.length - 1; i >= 0; i--) {
996
2548
  try {
997
2549
  Ammo.destroy(components.caches[i]);
@@ -1000,10 +2552,8 @@ ${errorMessage}
1000
2552
  }
1001
2553
  }
1002
2554
  components.caches.length = 0;
1003
- console.log("[MMDPlayerBase] \u2705 All btDbvtBroadphase destroyed");
1004
2555
  }
1005
2556
  if (components.dispatchers.length > 0) {
1006
- console.log(`[MMDPlayerBase] \u{1F5D1}\uFE0F Destroying ${components.dispatchers.length} btCollisionDispatcher(s)...`);
1007
2557
  for (let i = components.dispatchers.length - 1; i >= 0; i--) {
1008
2558
  try {
1009
2559
  Ammo.destroy(components.dispatchers[i]);
@@ -1012,10 +2562,8 @@ ${errorMessage}
1012
2562
  }
1013
2563
  }
1014
2564
  components.dispatchers.length = 0;
1015
- console.log("[MMDPlayerBase] \u2705 All btCollisionDispatcher destroyed");
1016
2565
  }
1017
2566
  if (components.configs.length > 0) {
1018
- console.log(`[MMDPlayerBase] \u{1F5D1}\uFE0F Destroying ${components.configs.length} btDefaultCollisionConfiguration(s)...`);
1019
2567
  for (let i = components.configs.length - 1; i >= 0; i--) {
1020
2568
  try {
1021
2569
  Ammo.destroy(components.configs[i]);
@@ -1024,19 +2572,14 @@ ${errorMessage}
1024
2572
  }
1025
2573
  }
1026
2574
  components.configs.length = 0;
1027
- console.log("[MMDPlayerBase] \u2705 All btDefaultCollisionConfiguration destroyed");
1028
2575
  }
1029
- console.log("[MMDPlayerBase] \u{1F389} Physics components cleanup completed!");
1030
2576
  } else {
1031
2577
  console.warn("[MMDPlayerBase] \u26A0\uFE0F Ammo.destroy not available, skipping physics cleanup");
1032
2578
  }
1033
- console.log("[MMDPlayerBase] Checking helper-level physics...");
1034
2579
  if (helperRef.current.sharedPhysics) {
1035
- console.log("[MMDPlayerBase] Clearing sharedPhysics reference...");
1036
2580
  helperRef.current.sharedPhysics = null;
1037
2581
  }
1038
2582
  if (helperRef.current.masterPhysics) {
1039
- console.log("[MMDPlayerBase] Clearing masterPhysics reference...");
1040
2583
  helperRef.current.masterPhysics = null;
1041
2584
  }
1042
2585
  if (helperRef.current.dispose) {
@@ -1057,8 +2600,8 @@ ${errorMessage}
1057
2600
  }
1058
2601
  if (sceneRef.current) {
1059
2602
  sceneRef.current.traverse((object) => {
1060
- if (object instanceof THREE.Mesh || object instanceof THREE.SkinnedMesh) {
1061
- if (object instanceof THREE.SkinnedMesh) {
2603
+ if (object instanceof THREE3.Mesh || object instanceof THREE3.SkinnedMesh) {
2604
+ if (object instanceof THREE3.SkinnedMesh) {
1062
2605
  if (object.skeleton) {
1063
2606
  object.skeleton.dispose();
1064
2607
  }
@@ -1110,7 +2653,7 @@ ${errorMessage}
1110
2653
  object.material = null;
1111
2654
  }
1112
2655
  }
1113
- if (object instanceof THREE.AudioListener) {
2656
+ if (object instanceof THREE3.AudioListener) {
1114
2657
  try {
1115
2658
  if (object.context && object.context.state !== "closed") {
1116
2659
  object.context.close?.();
@@ -1119,7 +2662,7 @@ ${errorMessage}
1119
2662
  console.warn("[MMDPlayerBase] Error closing AudioContext:", e);
1120
2663
  }
1121
2664
  }
1122
- if (object instanceof THREE.Light) {
2665
+ if (object instanceof THREE3.Light) {
1123
2666
  if (object.shadow && object.shadow.map) {
1124
2667
  object.shadow.map.dispose();
1125
2668
  object.shadow.map = null;
@@ -1135,13 +2678,13 @@ ${errorMessage}
1135
2678
  }
1136
2679
  if (rendererRef.current) {
1137
2680
  try {
1138
- if (composerRef.current) {
1139
- composerRef.current.passes.forEach((pass) => {
1140
- if (pass.dispose) pass.dispose();
1141
- });
1142
- composerRef.current = null;
1143
- }
1144
2681
  outlineEffectRef.current = null;
2682
+ if (multiFXAdapterRef.current) {
2683
+ multiFXAdapterRef.current.clear();
2684
+ multiFXAdapterRef.current = null;
2685
+ }
2686
+ fxEffectRef.current = null;
2687
+ fxAdapterRef.current = null;
1145
2688
  const renderer = rendererRef.current;
1146
2689
  if (renderer.renderLists) {
1147
2690
  renderer.renderLists.dispose();
@@ -1179,7 +2722,7 @@ ${errorMessage}
1179
2722
  rendererRef.current = null;
1180
2723
  }
1181
2724
  cameraRef.current = null;
1182
- clockRef.current = new THREE.Clock();
2725
+ clockRef.current = new THREE3.Clock();
1183
2726
  durationRef.current = 0;
1184
2727
  console.log("[MMDPlayerBase] Cleanup completed");
1185
2728
  if (typeof window !== "undefined" && "gc" in window) {
@@ -1200,12 +2743,11 @@ ${errorMessage}
1200
2743
  if (oldSound.parent) oldSound.parent.remove(oldSound);
1201
2744
  audioRef.current = null;
1202
2745
  }
1203
- console.log("[MMDPlayerBase] Loading new audio track:", resources.audioPath);
1204
2746
  audioLoaderRef.current.load(
1205
2747
  resources.audioPath,
1206
2748
  (buffer) => {
1207
2749
  if (!audioListenerRef.current) return;
1208
- const sound = new THREE.Audio(listener);
2750
+ const sound = new THREE3.Audio(listener);
1209
2751
  sound.setBuffer(buffer);
1210
2752
  sound.setLoop(loopRef.current);
1211
2753
  sound.setVolume(volume);
@@ -1226,7 +2768,7 @@ ${errorMessage}
1226
2768
  useEffect(() => {
1227
2769
  if (!sceneRef.current) return;
1228
2770
  if (showAxes && !axesHelperRef.current) {
1229
- const axesHelper = new THREE.AxesHelper(20);
2771
+ const axesHelper = new THREE3.AxesHelper(20);
1230
2772
  sceneRef.current.add(axesHelper);
1231
2773
  axesHelperRef.current = axesHelper;
1232
2774
  } else if (!showAxes && axesHelperRef.current) {
@@ -1244,10 +2786,10 @@ ${errorMessage}
1244
2786
  useEffect(() => {
1245
2787
  if (outlineEffectRef.current) {
1246
2788
  outlineEffectRef.current.defaultThickness = outlineOptions.thickness ?? 3e-3;
1247
- outlineEffectRef.current.defaultColor = new THREE.Color(outlineOptions.color ?? "#000000").toArray();
2789
+ outlineEffectRef.current.defaultColor = new THREE3.Color(outlineOptions.color ?? "#000000").toArray();
1248
2790
  if (sceneRef.current) {
1249
2791
  sceneRef.current.traverse((obj) => {
1250
- if (obj instanceof THREE.Mesh || obj instanceof THREE.SkinnedMesh) {
2792
+ if (obj instanceof THREE3.Mesh || obj instanceof THREE3.SkinnedMesh) {
1251
2793
  const materials = Array.isArray(obj.material) ? obj.material : [obj.material];
1252
2794
  materials.forEach((m) => {
1253
2795
  if (m.userData && m.userData.outlineParameters) {
@@ -1255,7 +2797,7 @@ ${errorMessage}
1255
2797
  m.userData.outlineParameters.thickness = outlineOptions.thickness;
1256
2798
  }
1257
2799
  if (outlineOptions.color !== void 0) {
1258
- m.userData.outlineParameters.color = new THREE.Color(outlineOptions.color).toArray();
2800
+ m.userData.outlineParameters.color = new THREE3.Color(outlineOptions.color).toArray();
1259
2801
  }
1260
2802
  }
1261
2803
  });
@@ -1263,36 +2805,7 @@ ${errorMessage}
1263
2805
  });
1264
2806
  }
1265
2807
  }
1266
- if (composerRef.current) {
1267
- const bloomPass = composerRef.current.passes.find((p) => p instanceof UnrealBloomPass);
1268
- if (bloomPass) {
1269
- bloomPass.strength = bloomOptions.strength ?? 1;
1270
- bloomPass.radius = bloomOptions.radius ?? 0.4;
1271
- bloomPass.threshold = bloomOptions.threshold ?? 0.8;
1272
- }
1273
- }
1274
- }, [outlineOptions.thickness, outlineOptions.color, bloomOptions.strength, bloomOptions.radius, bloomOptions.threshold]);
1275
- useEffect(() => {
1276
- if (!sceneRef.current) return;
1277
- sceneRef.current.traverse((obj) => {
1278
- if (obj instanceof THREE.Mesh || obj instanceof THREE.SkinnedMesh) {
1279
- const materials = Array.isArray(obj.material) ? obj.material : [obj.material];
1280
- materials.forEach((m) => {
1281
- if (m instanceof THREE.MeshPhongMaterial) {
1282
- if (toonOptions.enabled !== false && (toonOptions.enabled || renderEffect.includes("outline"))) {
1283
- m.shininess = toonOptions.shininess ?? 0;
1284
- m.specular.setScalar(0);
1285
- if (toonOptions.forceHardShading && m.toonMap) {
1286
- m.toonMap.magFilter = THREE.NearestFilter;
1287
- m.toonMap.minFilter = THREE.NearestFilter;
1288
- m.toonMap.needsUpdate = true;
1289
- }
1290
- }
1291
- }
1292
- });
1293
- }
1294
- });
1295
- }, [toonOptions.enabled, toonOptions.shininess, toonOptions.forceHardShading, renderEffect]);
2808
+ }, [outlineOptions.thickness, outlineOptions.color]);
1296
2809
  useEffect(() => {
1297
2810
  if (!isReadyRef.current) return;
1298
2811
  if (sceneRef.current) {
@@ -1300,10 +2813,10 @@ ${errorMessage}
1300
2813
  if (stage.backgroundColor === "transparent") {
1301
2814
  sceneRef.current.background = null;
1302
2815
  } else {
1303
- sceneRef.current.background = new THREE.Color(stage.backgroundColor);
2816
+ sceneRef.current.background = new THREE3.Color(stage.backgroundColor);
1304
2817
  }
1305
2818
  } else if (stage.backgroundImage) {
1306
- const textureLoader = new THREE.TextureLoader();
2819
+ const textureLoader = new THREE3.TextureLoader();
1307
2820
  textureLoader.load(stage.backgroundImage, (texture) => {
1308
2821
  if (sceneRef.current) sceneRef.current.background = texture;
1309
2822
  });
@@ -1311,10 +2824,10 @@ ${errorMessage}
1311
2824
  }
1312
2825
  if (sceneRef.current) {
1313
2826
  sceneRef.current.traverse((obj) => {
1314
- if (obj instanceof THREE.AmbientLight && stage.ambientLightIntensity !== void 0) {
2827
+ if (obj instanceof THREE3.AmbientLight && stage.ambientLightIntensity !== void 0) {
1315
2828
  obj.intensity = stage.ambientLightIntensity;
1316
2829
  }
1317
- if (obj instanceof THREE.DirectionalLight && stage.directionalLightIntensity !== void 0) {
2830
+ if (obj instanceof THREE3.DirectionalLight && stage.directionalLightIntensity !== void 0) {
1318
2831
  obj.intensity = stage.directionalLightIntensity;
1319
2832
  }
1320
2833
  });
@@ -1345,18 +2858,14 @@ ${errorMessage}
1345
2858
  latestCallbacks.current.onEnded?.();
1346
2859
  }
1347
2860
  }
1348
- const useOutline = renderEffect === "outline" || renderEffect === "outline+bloom";
1349
- const useBloom = renderEffect === "bloom" || renderEffect === "outline+bloom";
1350
- if (useBloom && composerRef.current) {
1351
- composerRef.current.render();
1352
- } else if (useOutline && outlineEffectRef.current) {
2861
+ if (renderEffect === "outline" && outlineEffectRef.current) {
1353
2862
  outlineEffectRef.current.render(sceneRef.current, cameraRef.current);
1354
2863
  } else {
1355
2864
  rendererRef.current.render(sceneRef.current, cameraRef.current);
1356
2865
  }
1357
2866
  }
1358
2867
  };
1359
- return /* @__PURE__ */ React6.createElement(
2868
+ return /* @__PURE__ */ React21.createElement(
1360
2869
  "div",
1361
2870
  {
1362
2871
  ref: containerRef,
@@ -1395,78 +2904,78 @@ var ControlPanel = ({
1395
2904
  onNext,
1396
2905
  onResetCamera
1397
2906
  }) => {
1398
- return /* @__PURE__ */ React6.createElement("div", { className: "absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/80 to-transparent p-4 transition-opacity duration-300 hover:opacity-100" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between text-white" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center gap-2" }, showPrevNext && onPrevious && /* @__PURE__ */ React6.createElement(
2907
+ return /* @__PURE__ */ React21.createElement("div", { className: "absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/80 to-transparent p-4 transition-opacity duration-300 hover:opacity-100" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between text-white" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center gap-2" }, showPrevNext && onPrevious && /* @__PURE__ */ React21.createElement(
1399
2908
  "button",
1400
2909
  {
1401
2910
  onClick: onPrevious,
1402
2911
  className: "rounded-full p-2 hover:bg-white/20 transition-colors",
1403
2912
  title: "\u4E0A\u4E00\u4E2A"
1404
2913
  },
1405
- /* @__PURE__ */ React6.createElement(SkipBack, { size: 20 })
1406
- ), /* @__PURE__ */ React6.createElement(
2914
+ /* @__PURE__ */ React21.createElement(SkipBack, { size: 20 })
2915
+ ), /* @__PURE__ */ React21.createElement(
1407
2916
  "button",
1408
2917
  {
1409
2918
  onClick: onPlayPause,
1410
2919
  className: "rounded-full p-2 hover:bg-white/20 transition-colors",
1411
2920
  title: isPlaying ? "\u6682\u505C" : "\u64AD\u653E"
1412
2921
  },
1413
- isPlaying ? /* @__PURE__ */ React6.createElement(Pause, { size: 24 }) : /* @__PURE__ */ React6.createElement(Play, { size: 24 })
1414
- ), showPrevNext && onNext && /* @__PURE__ */ React6.createElement(
2922
+ isPlaying ? /* @__PURE__ */ React21.createElement(Pause, { size: 24 }) : /* @__PURE__ */ React21.createElement(Play, { size: 24 })
2923
+ ), showPrevNext && onNext && /* @__PURE__ */ React21.createElement(
1415
2924
  "button",
1416
2925
  {
1417
2926
  onClick: onNext,
1418
2927
  className: "rounded-full p-2 hover:bg-white/20 transition-colors",
1419
2928
  title: "\u4E0B\u4E00\u4E2A"
1420
2929
  },
1421
- /* @__PURE__ */ React6.createElement(SkipForward, { size: 20 })
1422
- )), /* @__PURE__ */ React6.createElement("div", { className: "flex items-center gap-4" }, (title || subtitle) && /* @__PURE__ */ React6.createElement("div", { className: "hidden text-sm font-medium opacity-80 md:block" }, title, subtitle && /* @__PURE__ */ React6.createElement("span", { className: "ml-2 text-xs opacity-60" }, subtitle)), onToggleListLoop && /* @__PURE__ */ React6.createElement(
2930
+ /* @__PURE__ */ React21.createElement(SkipForward, { size: 20 })
2931
+ )), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center gap-4" }, (title || subtitle) && /* @__PURE__ */ React21.createElement("div", { className: "hidden text-sm font-medium opacity-80 md:block" }, title, subtitle && /* @__PURE__ */ React21.createElement("span", { className: "ml-2 text-xs opacity-60" }, subtitle)), onToggleListLoop && /* @__PURE__ */ React21.createElement(
1423
2932
  "button",
1424
2933
  {
1425
2934
  onClick: onToggleListLoop,
1426
2935
  className: `rounded-full p-2 transition-colors ${isListLooping ? "bg-green-500/30 hover:bg-green-500/50" : "hover:bg-white/20"}`,
1427
2936
  title: isListLooping ? "\u5217\u8868\u5FAA\u73AF\uFF1A\u5F00\u542F" : "\u5217\u8868\u5FAA\u73AF\uFF1A\u5173\u95ED"
1428
2937
  },
1429
- /* @__PURE__ */ React6.createElement(Repeat, { size: 20 })
1430
- ), /* @__PURE__ */ React6.createElement(
2938
+ /* @__PURE__ */ React21.createElement(Repeat, { size: 20 })
2939
+ ), /* @__PURE__ */ React21.createElement(
1431
2940
  "button",
1432
2941
  {
1433
2942
  onClick: onToggleLoop,
1434
2943
  className: `rounded-full p-2 transition-colors ${isLooping ? "bg-blue-500/30 hover:bg-blue-500/50" : "hover:bg-white/20"}`,
1435
2944
  title: isLooping ? "\u5355\u66F2\u5FAA\u73AF\uFF1A\u5F00\u542F" : "\u5355\u66F2\u5FAA\u73AF\uFF1A\u5173\u95ED"
1436
2945
  },
1437
- /* @__PURE__ */ React6.createElement(Repeat1, { size: 20 })
1438
- ), isCameraManual && onResetCamera && /* @__PURE__ */ React6.createElement(
2946
+ /* @__PURE__ */ React21.createElement(Repeat1, { size: 20 })
2947
+ ), isCameraManual && onResetCamera && /* @__PURE__ */ React21.createElement(
1439
2948
  "button",
1440
2949
  {
1441
2950
  onClick: onResetCamera,
1442
2951
  className: "rounded-full p-2 bg-blue-500/30 text-blue-400 hover:bg-blue-500/50 hover:text-blue-300 transition-all animate-in zoom-in duration-300",
1443
2952
  title: "\u6062\u590D\u521D\u59CB\u89C6\u89D2"
1444
2953
  },
1445
- /* @__PURE__ */ React6.createElement(Camera, { size: 20 })
1446
- ), onToggleAxes && /* @__PURE__ */ React6.createElement(
2954
+ /* @__PURE__ */ React21.createElement(Camera, { size: 20 })
2955
+ ), onToggleAxes && /* @__PURE__ */ React21.createElement(
1447
2956
  "button",
1448
2957
  {
1449
2958
  onClick: onToggleAxes,
1450
2959
  className: `rounded-full p-2 transition-colors ${showAxes ? "bg-blue-500/30 hover:bg-blue-500/50" : "hover:bg-white/20"}`,
1451
2960
  title: "\u663E\u793A/\u9690\u85CF\u5750\u6807\u8F74"
1452
2961
  },
1453
- /* @__PURE__ */ React6.createElement(Grid3x3, { size: 20 })
1454
- ), showSettings && /* @__PURE__ */ React6.createElement(
2962
+ /* @__PURE__ */ React21.createElement(Grid3x3, { size: 20 })
2963
+ ), showSettings && /* @__PURE__ */ React21.createElement(
1455
2964
  "button",
1456
2965
  {
1457
2966
  onClick: onOpenSettings,
1458
2967
  className: "rounded-full p-2 hover:bg-white/20 transition-colors",
1459
2968
  title: "\u8D44\u6E90\u8BBE\u7F6E"
1460
2969
  },
1461
- /* @__PURE__ */ React6.createElement(Settings, { size: 20 })
1462
- ), /* @__PURE__ */ React6.createElement(
2970
+ /* @__PURE__ */ React21.createElement(Settings, { size: 20 })
2971
+ ), /* @__PURE__ */ React21.createElement(
1463
2972
  "button",
1464
2973
  {
1465
2974
  onClick: onToggleFullscreen,
1466
2975
  className: "rounded-full p-2 hover:bg-white/20 transition-colors",
1467
2976
  title: isFullscreen ? "\u9000\u51FA\u5168\u5C4F" : "\u5168\u5C4F"
1468
2977
  },
1469
- isFullscreen ? /* @__PURE__ */ React6.createElement(Minimize, { size: 20 }) : /* @__PURE__ */ React6.createElement(Maximize, { size: 20 })
2978
+ isFullscreen ? /* @__PURE__ */ React21.createElement(Minimize, { size: 20 }) : /* @__PURE__ */ React21.createElement(Maximize, { size: 20 })
1470
2979
  ))));
1471
2980
  };
1472
2981
  var SettingsPanel = ({
@@ -1481,44 +2990,44 @@ var SettingsPanel = ({
1481
2990
  }) => {
1482
2991
  const renderListMode = () => {
1483
2992
  if (!items) return null;
1484
- return /* @__PURE__ */ React6.createElement("div", { className: "grid grid-cols-1 gap-2 p-4 sm:grid-cols-2" }, items.map((item) => /* @__PURE__ */ React6.createElement(
2993
+ return /* @__PURE__ */ React21.createElement("div", { className: "grid grid-cols-1 gap-2 p-4 sm:grid-cols-2" }, items.map((item) => /* @__PURE__ */ React21.createElement(
1485
2994
  "button",
1486
2995
  {
1487
2996
  key: item.id,
1488
2997
  onClick: () => onSelectId?.(item.id),
1489
2998
  className: `group flex items-center gap-3 rounded-lg p-3 transition-all ${currentId === item.id ? "bg-blue-500/20 ring-1 ring-blue-500" : "bg-white/5 hover:bg-white/10"}`
1490
2999
  },
1491
- /* @__PURE__ */ React6.createElement("div", { className: "flex h-12 w-12 flex-shrink-0 items-center justify-center rounded bg-black/20 overflow-hidden" }, item.thumbnail ? /* @__PURE__ */ React6.createElement("img", { src: item.thumbnail, alt: item.name, className: "h-full w-full object-cover" }) : /* @__PURE__ */ React6.createElement(Video, { size: 20, className: "opacity-50" })),
1492
- /* @__PURE__ */ React6.createElement("div", { className: "flex-1 text-left" }, /* @__PURE__ */ React6.createElement("div", { className: `font-medium ${currentId === item.id ? "text-blue-400" : "text-white"}` }, item.name), item.description && /* @__PURE__ */ React6.createElement("div", { className: "text-xs text-white/50 truncate" }, item.description)),
1493
- currentId === item.id && /* @__PURE__ */ React6.createElement(Check, { size: 16, className: "text-blue-400" })
3000
+ /* @__PURE__ */ React21.createElement("div", { className: "flex h-12 w-12 flex-shrink-0 items-center justify-center rounded bg-black/20 overflow-hidden" }, item.thumbnail ? /* @__PURE__ */ React21.createElement("img", { src: item.thumbnail, alt: item.name, className: "h-full w-full object-cover" }) : /* @__PURE__ */ React21.createElement(Video, { size: 20, className: "opacity-50" })),
3001
+ /* @__PURE__ */ React21.createElement("div", { className: "flex-1 text-left" }, /* @__PURE__ */ React21.createElement("div", { className: `font-medium ${currentId === item.id ? "text-blue-400" : "text-white"}` }, item.name), item.description && /* @__PURE__ */ React21.createElement("div", { className: "text-xs text-white/50 truncate" }, item.description)),
3002
+ currentId === item.id && /* @__PURE__ */ React21.createElement(Check, { size: 16, className: "text-blue-400" })
1494
3003
  )));
1495
3004
  };
1496
3005
  const renderOptionGroup = (title, icon, type, list = [], currentVal) => {
1497
3006
  if (!list || list.length === 0) return null;
1498
- return /* @__PURE__ */ React6.createElement("div", { className: "mb-6" }, /* @__PURE__ */ React6.createElement("div", { className: "mb-3 flex items-center gap-2 text-sm font-medium text-white/70" }, icon, /* @__PURE__ */ React6.createElement("span", null, title)), /* @__PURE__ */ React6.createElement("div", { className: "grid grid-cols-2 gap-2 sm:grid-cols-3" }, list.map((opt) => /* @__PURE__ */ React6.createElement(
3007
+ return /* @__PURE__ */ React21.createElement("div", { className: "mb-6" }, /* @__PURE__ */ React21.createElement("div", { className: "mb-3 flex items-center gap-2 text-sm font-medium text-white/70" }, icon, /* @__PURE__ */ React21.createElement("span", null, title)), /* @__PURE__ */ React21.createElement("div", { className: "grid grid-cols-2 gap-2 sm:grid-cols-3" }, list.map((opt) => /* @__PURE__ */ React21.createElement(
1499
3008
  "button",
1500
3009
  {
1501
3010
  key: opt.id,
1502
3011
  onClick: () => onSelectOption?.(type, opt.id),
1503
3012
  className: `relative flex flex-col items-center gap-2 rounded-lg p-2 text-center transition-all ${currentVal === opt.id ? "bg-blue-500/20 ring-1 ring-blue-500" : "bg-white/5 hover:bg-white/10"}`
1504
3013
  },
1505
- opt.thumbnail ? /* @__PURE__ */ React6.createElement("img", { src: opt.thumbnail, alt: opt.name, className: "h-16 w-full rounded object-cover bg-black/20" }) : /* @__PURE__ */ React6.createElement("div", { className: "flex h-16 w-full items-center justify-center rounded bg-black/20" }, /* @__PURE__ */ React6.createElement("div", { className: "text-xs opacity-30" }, opt.name.slice(0, 2))),
1506
- /* @__PURE__ */ React6.createElement("div", { className: `w-full truncate text-xs ${currentVal === opt.id ? "text-blue-400" : "text-white/80"}` }, opt.name),
1507
- currentVal === opt.id && /* @__PURE__ */ React6.createElement("div", { className: "absolute top-1 right-1 rounded-full bg-blue-500 p-0.5" }, /* @__PURE__ */ React6.createElement(Check, { size: 10, className: "text-white" }))
3014
+ opt.thumbnail ? /* @__PURE__ */ React21.createElement("img", { src: opt.thumbnail, alt: opt.name, className: "h-16 w-full rounded object-cover bg-black/20" }) : /* @__PURE__ */ React21.createElement("div", { className: "flex h-16 w-full items-center justify-center rounded bg-black/20" }, /* @__PURE__ */ React21.createElement("div", { className: "text-xs opacity-30" }, opt.name.slice(0, 2))),
3015
+ /* @__PURE__ */ React21.createElement("div", { className: `w-full truncate text-xs ${currentVal === opt.id ? "text-blue-400" : "text-white/80"}` }, opt.name),
3016
+ currentVal === opt.id && /* @__PURE__ */ React21.createElement("div", { className: "absolute top-1 right-1 rounded-full bg-blue-500 p-0.5" }, /* @__PURE__ */ React21.createElement(Check, { size: 10, className: "text-white" }))
1508
3017
  ))));
1509
3018
  };
1510
3019
  const renderOptionsMode = () => {
1511
3020
  if (!options) return null;
1512
- return /* @__PURE__ */ React6.createElement("div", { className: "p-4" }, renderOptionGroup("\u6A21\u578B", /* @__PURE__ */ React6.createElement(User, { size: 16 }), "models", options.models, currentSelection?.modelId), renderOptionGroup("\u52A8\u4F5C", /* @__PURE__ */ React6.createElement(Video, { size: 16 }), "motions", options.motions, currentSelection?.motionId), renderOptionGroup("\u955C\u5934", /* @__PURE__ */ React6.createElement(Image$1, { size: 16 }), "cameras", options.cameras, currentSelection?.cameraId), renderOptionGroup("\u97F3\u9891", /* @__PURE__ */ React6.createElement(Music, { size: 16 }), "audios", options.audios, currentSelection?.audioId), renderOptionGroup("\u821E\u53F0", /* @__PURE__ */ React6.createElement(Image$1, { size: 16 }), "stages", options.stages, currentSelection?.stageId));
3021
+ return /* @__PURE__ */ React21.createElement("div", { className: "p-4" }, renderOptionGroup("\u6A21\u578B", /* @__PURE__ */ React21.createElement(User, { size: 16 }), "models", options.models, currentSelection?.modelId), renderOptionGroup("\u52A8\u4F5C", /* @__PURE__ */ React21.createElement(Video, { size: 16 }), "motions", options.motions, currentSelection?.motionId), renderOptionGroup("\u955C\u5934", /* @__PURE__ */ React21.createElement(Image$1, { size: 16 }), "cameras", options.cameras, currentSelection?.cameraId), renderOptionGroup("\u97F3\u9891", /* @__PURE__ */ React21.createElement(Music, { size: 16 }), "audios", options.audios, currentSelection?.audioId), renderOptionGroup("\u821E\u53F0", /* @__PURE__ */ React21.createElement(Image$1, { size: 16 }), "stages", options.stages, currentSelection?.stageId));
1513
3022
  };
1514
- return /* @__PURE__ */ React6.createElement("div", { className: "absolute right-0 top-0 h-full w-full max-w-sm transform bg-[#1a1a1e]/95 backdrop-blur-md shadow-2xl transition-transform duration-300 ease-in-out overflow-y-auto z-20 border-l border-white/10" }, /* @__PURE__ */ React6.createElement("div", { className: "sticky top-0 z-10 flex items-center justify-between border-b border-white/10 bg-[#1a1a1e]/95 p-4 backdrop-blur-md" }, /* @__PURE__ */ React6.createElement("h2", { className: "text-lg font-semibold text-white" }, mode === "list" ? "\u64AD\u653E\u5217\u8868" : "\u81EA\u5B9A\u4E49\u573A\u666F"), /* @__PURE__ */ React6.createElement(
3023
+ return /* @__PURE__ */ React21.createElement("div", { className: "absolute right-0 top-0 h-full w-full max-w-sm transform bg-[#1a1a1e]/95 backdrop-blur-md shadow-2xl transition-transform duration-300 ease-in-out overflow-y-auto z-20 border-l border-white/10" }, /* @__PURE__ */ React21.createElement("div", { className: "sticky top-0 z-10 flex items-center justify-between border-b border-white/10 bg-[#1a1a1e]/95 p-4 backdrop-blur-md" }, /* @__PURE__ */ React21.createElement("h2", { className: "text-lg font-semibold text-white" }, mode === "list" ? "\u64AD\u653E\u5217\u8868" : "\u81EA\u5B9A\u4E49\u573A\u666F"), /* @__PURE__ */ React21.createElement(
1515
3024
  "button",
1516
3025
  {
1517
3026
  onClick: onClose,
1518
3027
  className: "rounded-full p-2 text-white/50 hover:bg-white/10 hover:text-white"
1519
3028
  },
1520
- /* @__PURE__ */ React6.createElement(X, { size: 20 })
1521
- )), /* @__PURE__ */ React6.createElement("div", { className: "pb-20" }, mode === "list" ? renderListMode() : renderOptionsMode()));
3029
+ /* @__PURE__ */ React21.createElement(X, { size: 20 })
3030
+ )), /* @__PURE__ */ React21.createElement("div", { className: "pb-20" }, mode === "list" ? renderListMode() : renderOptionsMode()));
1522
3031
  };
1523
3032
  var MMDPlayerEnhancedDebugInfo = ({
1524
3033
  isPlaying,
@@ -1543,7 +3052,7 @@ var MMDPlayerEnhancedDebugInfo = ({
1543
3052
  }, 1e3);
1544
3053
  return () => clearInterval(timer);
1545
3054
  }, []);
1546
- return /* @__PURE__ */ React6.createElement("div", { className: "text-white text-xs font-mono" }, /* @__PURE__ */ React6.createElement("h3", { className: "text-sm font-bold mb-3 pb-2 border-b border-gray-700" }, "\u{1F3AE} MMDPlayerEnhanced Debug"), /* @__PURE__ */ React6.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React6.createElement("h4", { className: "text-gray-400 mb-2" }, "\u64AD\u653E\u72B6\u6001"), /* @__PURE__ */ React6.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u64AD\u653E\u4E2D:"), /* @__PURE__ */ React6.createElement(StatusBadge, { active: isPlaying, label: isPlaying ? "Playing" : "Paused" })), /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u5FAA\u73AF:"), /* @__PURE__ */ React6.createElement(StatusBadge, { active: isLooping, label: isLooping ? "On" : "Off" })), /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u52A0\u8F7D\u4E2D:"), /* @__PURE__ */ React6.createElement(StatusBadge, { active: isLoading, label: isLoading ? "Loading" : "Ready" })))), /* @__PURE__ */ React6.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React6.createElement("h4", { className: "text-gray-400 mb-2" }, "\u89C6\u56FE\u72B6\u6001"), /* @__PURE__ */ React6.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u5168\u5C4F:"), /* @__PURE__ */ React6.createElement(StatusBadge, { active: isFullscreen, label: isFullscreen ? "Yes" : "No" })), /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u5750\u6807\u8F74:"), /* @__PURE__ */ React6.createElement(StatusBadge, { active: showAxes, label: showAxes ? "Show" : "Hide" })))), /* @__PURE__ */ React6.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React6.createElement("h4", { className: "text-gray-400 mb-2" }, "\u8D44\u6E90\u4FE1\u606F"), /* @__PURE__ */ React6.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u6A21\u5F0F:"), /* @__PURE__ */ React6.createElement("span", { className: "text-blue-400 uppercase" }, mode)), mode === "list" && /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u603B\u6570:"), /* @__PURE__ */ React6.createElement("span", { className: "text-green-400" }, totalResources)), currentResourceId && /* @__PURE__ */ React6.createElement("div", { className: "mt-2 p-2 bg-gray-800 rounded" }, /* @__PURE__ */ React6.createElement("div", { className: "text-gray-400 text-[10px]" }, "\u5F53\u524D\u8D44\u6E90"), /* @__PURE__ */ React6.createElement("div", { className: "text-white truncate" }, currentResourceName || currentResourceId), /* @__PURE__ */ React6.createElement("div", { className: "text-gray-500 text-[10px] mt-1 truncate" }, "ID: ", currentResourceId))))), memoryInfo && /* @__PURE__ */ React6.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React6.createElement("h4", { className: "text-gray-400 mb-2" }, "\u5185\u5B58\u76D1\u63A7 (Chrome only)"), /* @__PURE__ */ React6.createElement("div", { className: "space-y-2 p-2 bg-gray-800 rounded" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between text-[10px]" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u5DF2\u7528:"), /* @__PURE__ */ React6.createElement("span", { className: "text-yellow-400 font-bold" }, memoryInfo.used, " MB")), /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between text-[10px]" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u603B\u8BA1:"), /* @__PURE__ */ React6.createElement("span", { className: "text-blue-400" }, memoryInfo.total, " MB")), /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between text-[10px]" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u9650\u5236:"), /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, memoryInfo.limit, " MB")), /* @__PURE__ */ React6.createElement("div", { className: "mt-2" }, /* @__PURE__ */ React6.createElement("div", { className: "bg-gray-700 rounded-full h-2 overflow-hidden" }, /* @__PURE__ */ React6.createElement(
3055
+ return /* @__PURE__ */ React21.createElement("div", { className: "text-white text-xs font-mono" }, /* @__PURE__ */ React21.createElement("h3", { className: "text-sm font-bold mb-3 pb-2 border-b border-gray-700" }, "\u{1F3AE} MMDPlayerEnhanced Debug"), /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u64AD\u653E\u72B6\u6001"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u64AD\u653E\u4E2D:"), /* @__PURE__ */ React21.createElement(StatusBadge, { active: isPlaying, label: isPlaying ? "Playing" : "Paused" })), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u5FAA\u73AF:"), /* @__PURE__ */ React21.createElement(StatusBadge, { active: isLooping, label: isLooping ? "On" : "Off" })), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u52A0\u8F7D\u4E2D:"), /* @__PURE__ */ React21.createElement(StatusBadge, { active: isLoading, label: isLoading ? "Loading" : "Ready" })))), /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u89C6\u56FE\u72B6\u6001"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u5168\u5C4F:"), /* @__PURE__ */ React21.createElement(StatusBadge, { active: isFullscreen, label: isFullscreen ? "Yes" : "No" })), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u5750\u6807\u8F74:"), /* @__PURE__ */ React21.createElement(StatusBadge, { active: showAxes, label: showAxes ? "Show" : "Hide" })))), /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u8D44\u6E90\u4FE1\u606F"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u6A21\u5F0F:"), /* @__PURE__ */ React21.createElement("span", { className: "text-blue-400 uppercase" }, mode)), mode === "list" && /* @__PURE__ */ React21.createElement(React21.Fragment, null, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u603B\u6570:"), /* @__PURE__ */ React21.createElement("span", { className: "text-green-400" }, totalResources)), currentResourceId && /* @__PURE__ */ React21.createElement("div", { className: "mt-2 p-2 bg-gray-800 rounded" }, /* @__PURE__ */ React21.createElement("div", { className: "text-gray-400 text-[10px]" }, "\u5F53\u524D\u8D44\u6E90"), /* @__PURE__ */ React21.createElement("div", { className: "text-white truncate" }, currentResourceName || currentResourceId), /* @__PURE__ */ React21.createElement("div", { className: "text-gray-500 text-[10px] mt-1 truncate" }, "ID: ", currentResourceId))))), memoryInfo && /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u5185\u5B58\u76D1\u63A7 (Chrome only)"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-2 p-2 bg-gray-800 rounded" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between text-[10px]" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u5DF2\u7528:"), /* @__PURE__ */ React21.createElement("span", { className: "text-yellow-400 font-bold" }, memoryInfo.used, " MB")), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between text-[10px]" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u603B\u8BA1:"), /* @__PURE__ */ React21.createElement("span", { className: "text-blue-400" }, memoryInfo.total, " MB")), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between text-[10px]" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u9650\u5236:"), /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, memoryInfo.limit, " MB")), /* @__PURE__ */ React21.createElement("div", { className: "mt-2" }, /* @__PURE__ */ React21.createElement("div", { className: "bg-gray-700 rounded-full h-2 overflow-hidden" }, /* @__PURE__ */ React21.createElement(
1547
3056
  "div",
1548
3057
  {
1549
3058
  className: `h-full transition-all duration-300 ${parseFloat(memoryInfo.used) / parseFloat(memoryInfo.limit) * 100 > 80 ? "bg-red-500" : parseFloat(memoryInfo.used) / parseFloat(memoryInfo.limit) * 100 > 60 ? "bg-yellow-500" : "bg-green-500"}`,
@@ -1551,9 +3060,9 @@ var MMDPlayerEnhancedDebugInfo = ({
1551
3060
  width: `${Math.min(100, parseFloat(memoryInfo.used) / parseFloat(memoryInfo.limit) * 100)}%`
1552
3061
  }
1553
3062
  }
1554
- )), /* @__PURE__ */ React6.createElement("div", { className: "text-[9px] text-gray-500 mt-1 text-center" }, (parseFloat(memoryInfo.used) / parseFloat(memoryInfo.limit) * 100).toFixed(1), "%")))), /* @__PURE__ */ React6.createElement("div", { className: "mt-auto pt-4 border-t border-gray-700" }, /* @__PURE__ */ React6.createElement("div", { className: "text-gray-500 text-[10px]" }, "Last Update: ", (/* @__PURE__ */ new Date()).toLocaleTimeString())));
3063
+ )), /* @__PURE__ */ React21.createElement("div", { className: "text-[9px] text-gray-500 mt-1 text-center" }, (parseFloat(memoryInfo.used) / parseFloat(memoryInfo.limit) * 100).toFixed(1), "%")))), /* @__PURE__ */ React21.createElement("div", { className: "mt-auto pt-4 border-t border-gray-700" }, /* @__PURE__ */ React21.createElement("div", { className: "text-gray-500 text-[10px]" }, "Last Update: ", (/* @__PURE__ */ new Date()).toLocaleTimeString())));
1555
3064
  };
1556
- var StatusBadge = ({ active, label }) => /* @__PURE__ */ React6.createElement(
3065
+ var StatusBadge = ({ active, label }) => /* @__PURE__ */ React21.createElement(
1557
3066
  "span",
1558
3067
  {
1559
3068
  className: `px-2 py-0.5 rounded text-[10px] font-bold ${active ? "bg-green-600 text-white" : "bg-gray-700 text-gray-400"}`
@@ -1682,16 +3191,16 @@ var MMDPlayerEnhanced = ({
1682
3191
  });
1683
3192
  };
1684
3193
  if (!currentResources) {
1685
- return /* @__PURE__ */ React6.createElement("div", { className: "flex h-full w-full items-center justify-center bg-black text-white" }, "No Resources Configured");
3194
+ return /* @__PURE__ */ React21.createElement("div", { className: "flex h-full w-full items-center justify-center bg-black text-white" }, "No Resources Configured");
1686
3195
  }
1687
- return /* @__PURE__ */ React6.createElement(
3196
+ return /* @__PURE__ */ React21.createElement(
1688
3197
  "div",
1689
3198
  {
1690
3199
  ref: containerRef,
1691
3200
  className: `relative overflow-hidden bg-black group flex ${className}`,
1692
3201
  style
1693
3202
  },
1694
- /* @__PURE__ */ React6.createElement("div", { className: "flex-1 relative" }, /* @__PURE__ */ React6.createElement(
3203
+ /* @__PURE__ */ React21.createElement("div", { className: "flex-1 relative" }, /* @__PURE__ */ React21.createElement(
1695
3204
  MMDPlayerBase,
1696
3205
  {
1697
3206
  key: mode === "list" ? currentId : JSON.stringify(currentResources),
@@ -1723,7 +3232,7 @@ var MMDPlayerEnhanced = ({
1723
3232
  },
1724
3233
  ...rest
1725
3234
  }
1726
- ), isLoading && /* @__PURE__ */ React6.createElement("div", { className: "absolute inset-0 z-10 flex items-center justify-center bg-black/50 backdrop-blur-sm" }, /* @__PURE__ */ React6.createElement("div", { className: "h-10 w-10 animate-spin rounded-full border-4 border-white/20 border-t-blue-500" })), /* @__PURE__ */ React6.createElement("div", { className: `transition-opacity duration-300 ${isPlaying && !showSettings ? "opacity-0 group-hover:opacity-100" : "opacity-100"}` }, /* @__PURE__ */ React6.createElement(
3235
+ ), isLoading && /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 z-10 flex items-center justify-center bg-black/50 backdrop-blur-sm" }, /* @__PURE__ */ React21.createElement("div", { className: "h-10 w-10 animate-spin rounded-full border-4 border-white/20 border-t-blue-500" })), /* @__PURE__ */ React21.createElement("div", { className: `transition-opacity duration-300 ${isPlaying && !showSettings ? "opacity-0 group-hover:opacity-100" : "opacity-100"}` }, /* @__PURE__ */ React21.createElement(
1727
3236
  ControlPanel,
1728
3237
  {
1729
3238
  isPlaying,
@@ -1738,7 +3247,7 @@ var MMDPlayerEnhanced = ({
1738
3247
  onToggleAxes: () => setShowAxes(!showAxes),
1739
3248
  onOpenSettings: () => setShowSettings(true)
1740
3249
  }
1741
- )), showSettings && (mode === "list" || mode === "options") && /* @__PURE__ */ React6.createElement(
3250
+ )), showSettings && (mode === "list" || mode === "options") && /* @__PURE__ */ React21.createElement(
1742
3251
  SettingsPanel,
1743
3252
  {
1744
3253
  mode,
@@ -1751,7 +3260,7 @@ var MMDPlayerEnhanced = ({
1751
3260
  onClose: () => setShowSettings(false)
1752
3261
  }
1753
3262
  )),
1754
- showDebugInfo && /* @__PURE__ */ React6.createElement("div", { className: "w-96 bg-gray-900/95 border-l border-gray-700 p-4 overflow-y-auto" }, /* @__PURE__ */ React6.createElement(
3263
+ showDebugInfo && /* @__PURE__ */ React21.createElement("div", { className: "w-96 bg-gray-900/95 border-l border-gray-700 p-4 overflow-y-auto" }, /* @__PURE__ */ React21.createElement(
1755
3264
  MMDPlayerEnhancedDebugInfo,
1756
3265
  {
1757
3266
  isPlaying,
@@ -1793,14 +3302,14 @@ var MMDPlaylistDebugInfo = ({
1793
3302
  }, 1e3);
1794
3303
  return () => clearInterval(timer);
1795
3304
  }, []);
1796
- return /* @__PURE__ */ React6.createElement("div", { className: "text-white text-xs font-mono" }, /* @__PURE__ */ React6.createElement("h3", { className: "text-sm font-bold mb-3 pb-2 border-b border-gray-700" }, "\u{1F3AD} MMDPlaylist Debug"), /* @__PURE__ */ React6.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React6.createElement("h4", { className: "text-gray-400 mb-2" }, "\u64AD\u653E\u5217\u8868"), /* @__PURE__ */ React6.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React6.createElement("div", { className: "text-white truncate" }, playlistName), /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u8FDB\u5EA6:"), /* @__PURE__ */ React6.createElement("span", { className: "text-blue-400" }, currentIndex + 1, " / ", totalNodes)), /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u5217\u8868\u5FAA\u73AF:"), /* @__PURE__ */ React6.createElement(StatusBadge2, { active: isListLooping, label: isListLooping ? "On" : "Off" })))), /* @__PURE__ */ React6.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React6.createElement("h4", { className: "text-gray-400 mb-2" }, "\u5F53\u524D\u8282\u70B9"), /* @__PURE__ */ React6.createElement("div", { className: "p-2 bg-gray-800 rounded space-y-1" }, /* @__PURE__ */ React6.createElement("div", { className: "text-white font-semibold truncate" }, currentNode.name), /* @__PURE__ */ React6.createElement("div", { className: "text-gray-500 text-[10px] truncate" }, "ID: ", currentNode.id), currentNode.duration && /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u65F6\u957F:"), /* @__PURE__ */ React6.createElement("span", { className: "text-green-400" }, currentNode.duration, "s")), /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u8282\u70B9\u5FAA\u73AF:"), /* @__PURE__ */ React6.createElement(StatusBadge2, { active: isNodeLooping, label: isNodeLooping ? "On" : "Off" })))), /* @__PURE__ */ React6.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React6.createElement("h4", { className: "text-gray-400 mb-2" }, "\u64AD\u653E\u72B6\u6001"), /* @__PURE__ */ React6.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u64AD\u653E\u4E2D:"), /* @__PURE__ */ React6.createElement(StatusBadge2, { active: isPlaying, label: isPlaying ? "Playing" : "Paused" })), /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u52A0\u8F7D\u4E2D:"), /* @__PURE__ */ React6.createElement(StatusBadge2, { active: isLoading, label: isLoading ? "Loading" : "Ready" })))), /* @__PURE__ */ React6.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React6.createElement("h4", { className: "text-gray-400 mb-2" }, "\u9884\u52A0\u8F7D\u7B56\u7565"), /* @__PURE__ */ React6.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u7B56\u7565:"), /* @__PURE__ */ React6.createElement("span", { className: `px-2 py-0.5 rounded text-[10px] font-bold uppercase ${preloadStrategy === "all" ? "bg-red-600 text-white" : preloadStrategy === "next" ? "bg-yellow-600 text-white" : "bg-gray-700 text-gray-400"}` }, preloadStrategy)), /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u5DF2\u9884\u52A0\u8F7D:"), /* @__PURE__ */ React6.createElement("span", { className: "text-purple-400" }, preloadedNodes.length)), preloadedNodes.length > 0 && /* @__PURE__ */ React6.createElement("div", { className: "mt-2 p-2 bg-gray-800 rounded" }, /* @__PURE__ */ React6.createElement("div", { className: "text-gray-400 text-[10px] mb-1" }, "\u9884\u52A0\u8F7D\u8282\u70B9\u7D22\u5F15"), /* @__PURE__ */ React6.createElement("div", { className: "flex flex-wrap gap-1" }, preloadedNodes.map((idx) => /* @__PURE__ */ React6.createElement(
3305
+ return /* @__PURE__ */ React21.createElement("div", { className: "text-white text-xs font-mono" }, /* @__PURE__ */ React21.createElement("h3", { className: "text-sm font-bold mb-3 pb-2 border-b border-gray-700" }, "\u{1F3AD} MMDPlaylist Debug"), /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u64AD\u653E\u5217\u8868"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React21.createElement("div", { className: "text-white truncate" }, playlistName), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u8FDB\u5EA6:"), /* @__PURE__ */ React21.createElement("span", { className: "text-blue-400" }, currentIndex + 1, " / ", totalNodes)), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u5217\u8868\u5FAA\u73AF:"), /* @__PURE__ */ React21.createElement(StatusBadge2, { active: isListLooping, label: isListLooping ? "On" : "Off" })))), /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u5F53\u524D\u8282\u70B9"), /* @__PURE__ */ React21.createElement("div", { className: "p-2 bg-gray-800 rounded space-y-1" }, /* @__PURE__ */ React21.createElement("div", { className: "text-white font-semibold truncate" }, currentNode.name), /* @__PURE__ */ React21.createElement("div", { className: "text-gray-500 text-[10px] truncate" }, "ID: ", currentNode.id), currentNode.duration && /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u65F6\u957F:"), /* @__PURE__ */ React21.createElement("span", { className: "text-green-400" }, currentNode.duration, "s")), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u8282\u70B9\u5FAA\u73AF:"), /* @__PURE__ */ React21.createElement(StatusBadge2, { active: isNodeLooping, label: isNodeLooping ? "On" : "Off" })))), /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u64AD\u653E\u72B6\u6001"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u64AD\u653E\u4E2D:"), /* @__PURE__ */ React21.createElement(StatusBadge2, { active: isPlaying, label: isPlaying ? "Playing" : "Paused" })), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u52A0\u8F7D\u4E2D:"), /* @__PURE__ */ React21.createElement(StatusBadge2, { active: isLoading, label: isLoading ? "Loading" : "Ready" })))), /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u9884\u52A0\u8F7D\u7B56\u7565"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u7B56\u7565:"), /* @__PURE__ */ React21.createElement("span", { className: `px-2 py-0.5 rounded text-[10px] font-bold uppercase ${preloadStrategy === "all" ? "bg-red-600 text-white" : preloadStrategy === "next" ? "bg-yellow-600 text-white" : "bg-gray-700 text-gray-400"}` }, preloadStrategy)), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u5DF2\u9884\u52A0\u8F7D:"), /* @__PURE__ */ React21.createElement("span", { className: "text-purple-400" }, preloadedNodes.length)), preloadedNodes.length > 0 && /* @__PURE__ */ React21.createElement("div", { className: "mt-2 p-2 bg-gray-800 rounded" }, /* @__PURE__ */ React21.createElement("div", { className: "text-gray-400 text-[10px] mb-1" }, "\u9884\u52A0\u8F7D\u8282\u70B9\u7D22\u5F15"), /* @__PURE__ */ React21.createElement("div", { className: "flex flex-wrap gap-1" }, preloadedNodes.map((idx) => /* @__PURE__ */ React21.createElement(
1797
3306
  "span",
1798
3307
  {
1799
3308
  key: idx,
1800
3309
  className: `px-1.5 py-0.5 rounded text-[10px] ${idx === currentIndex ? "bg-green-600 text-white font-bold" : "bg-gray-700 text-gray-300"}`
1801
3310
  },
1802
3311
  idx
1803
- )))))), /* @__PURE__ */ React6.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React6.createElement("h4", { className: "text-gray-400 mb-2" }, "\u89C6\u56FE\u72B6\u6001"), /* @__PURE__ */ React6.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u5168\u5C4F:"), /* @__PURE__ */ React6.createElement(StatusBadge2, { active: isFullscreen, label: isFullscreen ? "Yes" : "No" })), /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u5750\u6807\u8F74:"), /* @__PURE__ */ React6.createElement(StatusBadge2, { active: showAxes, label: showAxes ? "Show" : "Hide" })))), memoryInfo && /* @__PURE__ */ React6.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React6.createElement("h4", { className: "text-gray-400 mb-2" }, "\u5185\u5B58\u76D1\u63A7 (Chrome only)"), /* @__PURE__ */ React6.createElement("div", { className: "space-y-2 p-2 bg-gray-800 rounded" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between text-[10px]" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u5DF2\u7528:"), /* @__PURE__ */ React6.createElement("span", { className: "text-yellow-400 font-bold" }, memoryInfo.used, " MB")), /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between text-[10px]" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u603B\u8BA1:"), /* @__PURE__ */ React6.createElement("span", { className: "text-blue-400" }, memoryInfo.total, " MB")), /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between text-[10px]" }, /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, "\u9650\u5236:"), /* @__PURE__ */ React6.createElement("span", { className: "text-gray-400" }, memoryInfo.limit, " MB")), /* @__PURE__ */ React6.createElement("div", { className: "mt-2" }, /* @__PURE__ */ React6.createElement("div", { className: "bg-gray-700 rounded-full h-2 overflow-hidden" }, /* @__PURE__ */ React6.createElement(
3312
+ )))))), /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u89C6\u56FE\u72B6\u6001"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-1 pl-2" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u5168\u5C4F:"), /* @__PURE__ */ React21.createElement(StatusBadge2, { active: isFullscreen, label: isFullscreen ? "Yes" : "No" })), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u5750\u6807\u8F74:"), /* @__PURE__ */ React21.createElement(StatusBadge2, { active: showAxes, label: showAxes ? "Show" : "Hide" })))), memoryInfo && /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u5185\u5B58\u76D1\u63A7 (Chrome only)"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-2 p-2 bg-gray-800 rounded" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between text-[10px]" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u5DF2\u7528:"), /* @__PURE__ */ React21.createElement("span", { className: "text-yellow-400 font-bold" }, memoryInfo.used, " MB")), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between text-[10px]" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u603B\u8BA1:"), /* @__PURE__ */ React21.createElement("span", { className: "text-blue-400" }, memoryInfo.total, " MB")), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between text-[10px]" }, /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, "\u9650\u5236:"), /* @__PURE__ */ React21.createElement("span", { className: "text-gray-400" }, memoryInfo.limit, " MB")), /* @__PURE__ */ React21.createElement("div", { className: "mt-2" }, /* @__PURE__ */ React21.createElement("div", { className: "bg-gray-700 rounded-full h-2 overflow-hidden" }, /* @__PURE__ */ React21.createElement(
1804
3313
  "div",
1805
3314
  {
1806
3315
  className: `h-full transition-all duration-300 ${parseFloat(memoryInfo.used) / parseFloat(memoryInfo.limit) * 100 > 80 ? "bg-red-500" : parseFloat(memoryInfo.used) / parseFloat(memoryInfo.limit) * 100 > 60 ? "bg-yellow-500" : "bg-green-500"}`,
@@ -1808,18 +3317,18 @@ var MMDPlaylistDebugInfo = ({
1808
3317
  width: `${Math.min(100, parseFloat(memoryInfo.used) / parseFloat(memoryInfo.limit) * 100)}%`
1809
3318
  }
1810
3319
  }
1811
- )), /* @__PURE__ */ React6.createElement("div", { className: "text-[9px] text-gray-500 mt-1 text-center" }, (parseFloat(memoryInfo.used) / parseFloat(memoryInfo.limit) * 100).toFixed(1), "%")))), /* @__PURE__ */ React6.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React6.createElement("h4", { className: "text-gray-400 mb-2" }, "\u8282\u70B9\u5217\u8868"), /* @__PURE__ */ React6.createElement("div", { className: "space-y-1 max-h-40 overflow-y-auto" }, Array.from({ length: totalNodes }).map((_, idx) => /* @__PURE__ */ React6.createElement(
3320
+ )), /* @__PURE__ */ React21.createElement("div", { className: "text-[9px] text-gray-500 mt-1 text-center" }, (parseFloat(memoryInfo.used) / parseFloat(memoryInfo.limit) * 100).toFixed(1), "%")))), /* @__PURE__ */ React21.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React21.createElement("h4", { className: "text-gray-400 mb-2" }, "\u8282\u70B9\u5217\u8868"), /* @__PURE__ */ React21.createElement("div", { className: "space-y-1 max-h-40 overflow-y-auto" }, Array.from({ length: totalNodes }).map((_, idx) => /* @__PURE__ */ React21.createElement(
1812
3321
  "div",
1813
3322
  {
1814
3323
  key: idx,
1815
3324
  className: `px-2 py-1 rounded text-[10px] flex items-center justify-between ${idx === currentIndex ? "bg-blue-600 text-white font-bold" : preloadedNodes.includes(idx) ? "bg-yellow-900/50 text-yellow-300" : "bg-gray-800 text-gray-400"}`
1816
3325
  },
1817
- /* @__PURE__ */ React6.createElement("span", null, "\u8282\u70B9 ", idx),
1818
- idx === currentIndex && /* @__PURE__ */ React6.createElement("span", null, "\u25B6"),
1819
- preloadedNodes.includes(idx) && idx !== currentIndex && /* @__PURE__ */ React6.createElement("span", null, "\u23F3")
1820
- )))), /* @__PURE__ */ React6.createElement("div", { className: "mt-auto pt-4 border-t border-gray-700" }, /* @__PURE__ */ React6.createElement("div", { className: "text-gray-500 text-[10px]" }, "Last Update: ", (/* @__PURE__ */ new Date()).toLocaleTimeString())));
3326
+ /* @__PURE__ */ React21.createElement("span", null, "\u8282\u70B9 ", idx),
3327
+ idx === currentIndex && /* @__PURE__ */ React21.createElement("span", null, "\u25B6"),
3328
+ preloadedNodes.includes(idx) && idx !== currentIndex && /* @__PURE__ */ React21.createElement("span", null, "\u23F3")
3329
+ )))), /* @__PURE__ */ React21.createElement("div", { className: "mt-auto pt-4 border-t border-gray-700" }, /* @__PURE__ */ React21.createElement("div", { className: "text-gray-500 text-[10px]" }, "Last Update: ", (/* @__PURE__ */ new Date()).toLocaleTimeString())));
1821
3330
  };
1822
- var StatusBadge2 = ({ active, label }) => /* @__PURE__ */ React6.createElement(
3331
+ var StatusBadge2 = ({ active, label }) => /* @__PURE__ */ React21.createElement(
1823
3332
  "span",
1824
3333
  {
1825
3334
  className: `px-2 py-0.5 rounded text-[10px] font-bold ${active ? "bg-green-600 text-white" : "bg-gray-700 text-gray-400"}`
@@ -1985,17 +3494,17 @@ var MMDPlaylist = ({
1985
3494
  };
1986
3495
  }, []);
1987
3496
  if (!currentNode) {
1988
- return /* @__PURE__ */ React6.createElement("div", { className: "flex h-full w-full items-center justify-center bg-black text-white" }, "\u64AD\u653E\u5217\u8868\u4E3A\u7A7A");
3497
+ return /* @__PURE__ */ React21.createElement("div", { className: "flex h-full w-full items-center justify-center bg-black text-white" }, "\u64AD\u653E\u5217\u8868\u4E3A\u7A7A");
1989
3498
  }
1990
3499
  const showPrevNext = nodes.length > 1;
1991
- return /* @__PURE__ */ React6.createElement(
3500
+ return /* @__PURE__ */ React21.createElement(
1992
3501
  "div",
1993
3502
  {
1994
3503
  ref: containerRef,
1995
3504
  className: `relative overflow-hidden bg-black group flex h-full ${className}`,
1996
3505
  style
1997
3506
  },
1998
- /* @__PURE__ */ React6.createElement("div", { className: "flex-1 relative" }, !isTransitioning && /* @__PURE__ */ React6.createElement(
3507
+ /* @__PURE__ */ React21.createElement("div", { className: "flex-1 relative" }, !isTransitioning && /* @__PURE__ */ React21.createElement(
1999
3508
  MMDPlayerBase,
2000
3509
  {
2001
3510
  key: currentNode.id,
@@ -2018,12 +3527,12 @@ var MMDPlaylist = ({
2018
3527
  onEnded: handleEnded,
2019
3528
  onError
2020
3529
  }
2021
- ), (isLoading || isTransitioning) && /* @__PURE__ */ React6.createElement("div", { className: "absolute inset-0 z-10 flex items-center justify-center bg-black/50 backdrop-blur-sm" }, /* @__PURE__ */ React6.createElement("div", { className: "flex flex-col items-center gap-3" }, /* @__PURE__ */ React6.createElement("div", { className: "h-10 w-10 animate-spin rounded-full border-4 border-white/20 border-t-blue-500" }), /* @__PURE__ */ React6.createElement("div", { className: "text-sm text-white/80" }, isTransitioning ? "\u5207\u6362\u4E2D..." : `\u6B63\u5728\u52A0\u8F7D ${currentIndex + 1} / ${nodes.length}`))), /* @__PURE__ */ React6.createElement(
3530
+ ), (isLoading || isTransitioning) && /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 z-10 flex items-center justify-center bg-black/50 backdrop-blur-sm" }, /* @__PURE__ */ React21.createElement("div", { className: "flex flex-col items-center gap-3" }, /* @__PURE__ */ React21.createElement("div", { className: "h-10 w-10 animate-spin rounded-full border-4 border-white/20 border-t-blue-500" }), /* @__PURE__ */ React21.createElement("div", { className: "text-sm text-white/80" }, isTransitioning ? "\u5207\u6362\u4E2D..." : `\u6B63\u5728\u52A0\u8F7D ${currentIndex + 1} / ${nodes.length}`))), /* @__PURE__ */ React21.createElement(
2022
3531
  "div",
2023
3532
  {
2024
3533
  className: `transition-opacity duration-300 ${isPlaying && !showPlaylist ? "opacity-0 group-hover:opacity-100" : "opacity-100"}`
2025
3534
  },
2026
- /* @__PURE__ */ React6.createElement(
3535
+ /* @__PURE__ */ React21.createElement(
2027
3536
  ControlPanel,
2028
3537
  {
2029
3538
  isPlaying,
@@ -2050,15 +3559,15 @@ var MMDPlaylist = ({
2050
3559
  }
2051
3560
  }
2052
3561
  )
2053
- ), showPlaylist && /* @__PURE__ */ React6.createElement("div", { className: "absolute inset-0 z-20 flex items-end bg-black/80 backdrop-blur-sm" }, /* @__PURE__ */ React6.createElement("div", { className: "w-full max-h-[60vh] overflow-y-auto bg-gray-900/95 rounded-t-xl" }, /* @__PURE__ */ React6.createElement("div", { className: "sticky top-0 flex items-center justify-between bg-gray-800 px-4 py-3 border-b border-gray-700" }, /* @__PURE__ */ React6.createElement("div", null, /* @__PURE__ */ React6.createElement("h3", { className: "text-white font-semibold" }, playlist.name), /* @__PURE__ */ React6.createElement("p", { className: "text-xs text-gray-400 mt-0.5" }, "\u5171 ", nodes.length, " \u4E2A\u8282\u70B9")), /* @__PURE__ */ React6.createElement(
3562
+ ), showPlaylist && /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 z-20 flex items-end bg-black/80 backdrop-blur-sm" }, /* @__PURE__ */ React21.createElement("div", { className: "w-full max-h-[60vh] overflow-y-auto bg-gray-900/95 rounded-t-xl" }, /* @__PURE__ */ React21.createElement("div", { className: "sticky top-0 flex items-center justify-between bg-gray-800 px-4 py-3 border-b border-gray-700" }, /* @__PURE__ */ React21.createElement("div", null, /* @__PURE__ */ React21.createElement("h3", { className: "text-white font-semibold" }, playlist.name), /* @__PURE__ */ React21.createElement("p", { className: "text-xs text-gray-400 mt-0.5" }, "\u5171 ", nodes.length, " \u4E2A\u8282\u70B9")), /* @__PURE__ */ React21.createElement(
2054
3563
  "button",
2055
3564
  {
2056
3565
  onClick: () => setShowPlaylist(false),
2057
3566
  className: "p-2 hover:bg-white/10 rounded-lg transition-colors",
2058
3567
  "aria-label": "\u5173\u95ED\u64AD\u653E\u5217\u8868"
2059
3568
  },
2060
- /* @__PURE__ */ React6.createElement("svg", { className: "w-5 h-5 text-white", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor" }, /* @__PURE__ */ React6.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }))
2061
- )), /* @__PURE__ */ React6.createElement("div", { className: "p-2" }, nodes.map((node, index) => /* @__PURE__ */ React6.createElement(
3569
+ /* @__PURE__ */ React21.createElement("svg", { className: "w-5 h-5 text-white", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor" }, /* @__PURE__ */ React21.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }))
3570
+ )), /* @__PURE__ */ React21.createElement("div", { className: "p-2" }, nodes.map((node, index) => /* @__PURE__ */ React21.createElement(
2062
3571
  "button",
2063
3572
  {
2064
3573
  key: node.id,
@@ -2068,17 +3577,17 @@ var MMDPlaylist = ({
2068
3577
  },
2069
3578
  className: `w-full flex items-center gap-3 p-3 rounded-lg mb-2 transition-all ${index === currentIndex ? "bg-blue-600 text-white" : "bg-gray-800 text-gray-300 hover:bg-gray-700"}`
2070
3579
  },
2071
- /* @__PURE__ */ React6.createElement(
3580
+ /* @__PURE__ */ React21.createElement(
2072
3581
  "div",
2073
3582
  {
2074
3583
  className: `flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center text-sm font-semibold ${index === currentIndex ? "bg-white/20" : "bg-gray-700"}`
2075
3584
  },
2076
3585
  index + 1
2077
3586
  ),
2078
- /* @__PURE__ */ React6.createElement("div", { className: "flex-1 text-left" }, /* @__PURE__ */ React6.createElement("div", { className: "font-medium" }, node.name), node.duration && /* @__PURE__ */ React6.createElement("div", { className: "text-xs opacity-75 mt-0.5" }, Math.floor(node.duration / 60), ":", String(Math.floor(node.duration % 60)).padStart(2, "0"))),
2079
- index === currentIndex && /* @__PURE__ */ React6.createElement("div", { className: "flex-shrink-0" }, /* @__PURE__ */ React6.createElement("svg", { className: "w-5 h-5", fill: "currentColor", viewBox: "0 0 24 24" }, /* @__PURE__ */ React6.createElement("path", { d: "M8 5v14l11-7z" })))
3587
+ /* @__PURE__ */ React21.createElement("div", { className: "flex-1 text-left" }, /* @__PURE__ */ React21.createElement("div", { className: "font-medium" }, node.name), node.duration && /* @__PURE__ */ React21.createElement("div", { className: "text-xs opacity-75 mt-0.5" }, Math.floor(node.duration / 60), ":", String(Math.floor(node.duration % 60)).padStart(2, "0"))),
3588
+ index === currentIndex && /* @__PURE__ */ React21.createElement("div", { className: "flex-shrink-0" }, /* @__PURE__ */ React21.createElement("svg", { className: "w-5 h-5", fill: "currentColor", viewBox: "0 0 24 24" }, /* @__PURE__ */ React21.createElement("path", { d: "M8 5v14l11-7z" })))
2080
3589
  )))))),
2081
- showDebugInfo && /* @__PURE__ */ React6.createElement("div", { className: "w-96 flex-shrink-0 bg-gray-900/95 border-l border-gray-700 p-4 overflow-y-auto h-full" }, /* @__PURE__ */ React6.createElement(
3590
+ showDebugInfo && /* @__PURE__ */ React21.createElement("div", { className: "w-96 flex-shrink-0 bg-gray-900/95 border-l border-gray-700 p-4 overflow-y-auto h-full" }, /* @__PURE__ */ React21.createElement(
2082
3591
  MMDPlaylistDebugInfo,
2083
3592
  {
2084
3593
  playlistName: playlist.name,
@@ -2224,7 +3733,7 @@ var DialogueBox = ({
2224
3733
  displayedText,
2225
3734
  isComplete
2226
3735
  });
2227
- const dialogueContent = /* @__PURE__ */ React6.createElement(
3736
+ const dialogueContent = /* @__PURE__ */ React21.createElement(
2228
3737
  "div",
2229
3738
  {
2230
3739
  className: `${className || ""}`,
@@ -2240,7 +3749,7 @@ var DialogueBox = ({
2240
3749
  pointerEvents: "auto"
2241
3750
  }
2242
3751
  },
2243
- /* @__PURE__ */ React6.createElement(
3752
+ /* @__PURE__ */ React21.createElement(
2244
3753
  "div",
2245
3754
  {
2246
3755
  className: "w-full h-full rounded-t-3xl border cursor-pointer select-none transition-all hover:border-white/50 hover:shadow-2xl flex flex-col relative overflow-hidden",
@@ -2265,7 +3774,7 @@ var DialogueBox = ({
2265
3774
  `
2266
3775
  }
2267
3776
  },
2268
- /* @__PURE__ */ React6.createElement(
3777
+ /* @__PURE__ */ React21.createElement(
2269
3778
  "div",
2270
3779
  {
2271
3780
  className: "absolute inset-0 opacity-20 pointer-events-none",
@@ -2281,7 +3790,7 @@ var DialogueBox = ({
2281
3790
  }
2282
3791
  }
2283
3792
  ),
2284
- /* @__PURE__ */ React6.createElement(
3793
+ /* @__PURE__ */ React21.createElement(
2285
3794
  "div",
2286
3795
  {
2287
3796
  className: "absolute top-0 left-0 right-0 h-1",
@@ -2290,7 +3799,7 @@ var DialogueBox = ({
2290
3799
  }
2291
3800
  }
2292
3801
  ),
2293
- /* @__PURE__ */ React6.createElement(
3802
+ /* @__PURE__ */ React21.createElement(
2294
3803
  "div",
2295
3804
  {
2296
3805
  className: "absolute bottom-0 left-0 right-0 h-px",
@@ -2299,7 +3808,7 @@ var DialogueBox = ({
2299
3808
  }
2300
3809
  }
2301
3810
  ),
2302
- showControls && /* @__PURE__ */ React6.createElement("div", { className: "flex justify-end gap-3 px-6 pt-4 pb-2 flex-shrink-0 relative z-10" }, showHistoryButton && /* @__PURE__ */ React6.createElement(
3811
+ showControls && /* @__PURE__ */ React21.createElement("div", { className: "flex justify-end gap-3 px-6 pt-4 pb-2 flex-shrink-0 relative z-10" }, showHistoryButton && /* @__PURE__ */ React21.createElement(
2303
3812
  "button",
2304
3813
  {
2305
3814
  onClick: (e) => {
@@ -2314,7 +3823,7 @@ var DialogueBox = ({
2314
3823
  title: "\u5386\u53F2\u8BB0\u5F55"
2315
3824
  },
2316
3825
  "\u{1F4DC} \u5386\u53F2"
2317
- ), isCameraManual && /* @__PURE__ */ React6.createElement(
3826
+ ), isCameraManual && /* @__PURE__ */ React21.createElement(
2318
3827
  "button",
2319
3828
  {
2320
3829
  onClick: (e) => {
@@ -2329,7 +3838,7 @@ var DialogueBox = ({
2329
3838
  title: "\u6062\u590D\u521D\u59CB\u89C6\u89D2"
2330
3839
  },
2331
3840
  "\u{1F3A5} \u6062\u590D\u89C6\u89D2"
2332
- ), showAutoButton && /* @__PURE__ */ React6.createElement(
3841
+ ), showAutoButton && /* @__PURE__ */ React21.createElement(
2333
3842
  "button",
2334
3843
  {
2335
3844
  onClick: (e) => {
@@ -2344,7 +3853,7 @@ var DialogueBox = ({
2344
3853
  title: "\u81EA\u52A8\u64AD\u653E"
2345
3854
  },
2346
3855
  "\u25B6 \u81EA\u52A8"
2347
- ), showSkipButton && /* @__PURE__ */ React6.createElement(
3856
+ ), showSkipButton && /* @__PURE__ */ React21.createElement(
2348
3857
  "button",
2349
3858
  {
2350
3859
  onClick: (e) => {
@@ -2360,7 +3869,7 @@ var DialogueBox = ({
2360
3869
  },
2361
3870
  "\u23E9 \u5FEB\u8FDB"
2362
3871
  )),
2363
- /* @__PURE__ */ React6.createElement("div", { className: "px-8 pb-6 flex-1 flex flex-col justify-center overflow-y-auto relative z-10" }, dialogue.speaker && /* @__PURE__ */ React6.createElement(
3872
+ /* @__PURE__ */ React21.createElement("div", { className: "px-8 pb-6 flex-1 flex flex-col justify-center overflow-y-auto relative z-10" }, dialogue.speaker && /* @__PURE__ */ React21.createElement(
2364
3873
  "div",
2365
3874
  {
2366
3875
  className: "inline-block px-6 py-2.5 rounded-2xl mb-4 text-sm font-bold shadow-2xl self-start relative overflow-hidden transition-all hover:scale-105",
@@ -2375,7 +3884,7 @@ var DialogueBox = ({
2375
3884
  border: "1px solid rgba(255, 255, 255, 0.3)"
2376
3885
  }
2377
3886
  },
2378
- /* @__PURE__ */ React6.createElement(
3887
+ /* @__PURE__ */ React21.createElement(
2379
3888
  "div",
2380
3889
  {
2381
3890
  className: "absolute inset-0 opacity-30",
@@ -2386,8 +3895,8 @@ var DialogueBox = ({
2386
3895
  }
2387
3896
  }
2388
3897
  ),
2389
- /* @__PURE__ */ React6.createElement("span", { className: "relative z-10 drop-shadow-lg" }, dialogue.speaker)
2390
- ), /* @__PURE__ */ React6.createElement(
3898
+ /* @__PURE__ */ React21.createElement("span", { className: "relative z-10 drop-shadow-lg" }, dialogue.speaker)
3899
+ ), /* @__PURE__ */ React21.createElement(
2391
3900
  "div",
2392
3901
  {
2393
3902
  className: "text-lg leading-relaxed relative",
@@ -2396,10 +3905,10 @@ var DialogueBox = ({
2396
3905
  textShadow: "0 2px 8px rgba(0, 0, 0, 0.5)"
2397
3906
  }
2398
3907
  },
2399
- /* @__PURE__ */ React6.createElement("span", { className: "inline-block", style: {
3908
+ /* @__PURE__ */ React21.createElement("span", { className: "inline-block", style: {
2400
3909
  animation: !isComplete ? "textFadeIn 0.3s ease-out" : "none"
2401
3910
  } }, displayedText),
2402
- !isComplete && /* @__PURE__ */ React6.createElement(
3911
+ !isComplete && /* @__PURE__ */ React21.createElement(
2403
3912
  "span",
2404
3913
  {
2405
3914
  className: "inline-block w-1 h-6 ml-1 align-middle",
@@ -2410,7 +3919,7 @@ var DialogueBox = ({
2410
3919
  }
2411
3920
  }
2412
3921
  )
2413
- ), isComplete && theme.showContinueHint && /* @__PURE__ */ React6.createElement("div", { className: "flex justify-end mt-4" }, /* @__PURE__ */ React6.createElement(
3922
+ ), isComplete && theme.showContinueHint && /* @__PURE__ */ React21.createElement("div", { className: "flex justify-end mt-4" }, /* @__PURE__ */ React21.createElement(
2414
3923
  "span",
2415
3924
  {
2416
3925
  className: "text-sm px-4 py-2 rounded-full backdrop-blur-lg font-medium",
@@ -2448,7 +3957,7 @@ var HistoryPanel = ({
2448
3957
  useEffect(() => {
2449
3958
  setIsMounted(true);
2450
3959
  }, []);
2451
- const historyContent = /* @__PURE__ */ React6.createElement(
3960
+ const historyContent = /* @__PURE__ */ React21.createElement(
2452
3961
  "div",
2453
3962
  {
2454
3963
  className: `fixed inset-0 flex flex-col ${className}`,
@@ -2459,7 +3968,7 @@ var HistoryPanel = ({
2459
3968
  },
2460
3969
  onClick: onClose
2461
3970
  },
2462
- /* @__PURE__ */ React6.createElement(
3971
+ /* @__PURE__ */ React21.createElement(
2463
3972
  "div",
2464
3973
  {
2465
3974
  className: "absolute inset-0 opacity-25 pointer-events-none",
@@ -2475,7 +3984,7 @@ var HistoryPanel = ({
2475
3984
  }
2476
3985
  }
2477
3986
  ),
2478
- /* @__PURE__ */ React6.createElement(
3987
+ /* @__PURE__ */ React21.createElement(
2479
3988
  "div",
2480
3989
  {
2481
3990
  className: "absolute inset-0 pointer-events-none",
@@ -2485,7 +3994,7 @@ var HistoryPanel = ({
2485
3994
  }
2486
3995
  }
2487
3996
  ),
2488
- /* @__PURE__ */ React6.createElement("div", { className: "relative z-10 flex flex-col h-full", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React6.createElement(
3997
+ /* @__PURE__ */ React21.createElement("div", { className: "relative z-10 flex flex-col h-full", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React21.createElement(
2489
3998
  "div",
2490
3999
  {
2491
4000
  className: "flex items-center justify-between px-8 py-6 border-b relative",
@@ -2497,8 +4006,8 @@ var HistoryPanel = ({
2497
4006
  boxShadow: "0 4px 24px rgba(255, 255, 255, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.3)"
2498
4007
  }
2499
4008
  },
2500
- /* @__PURE__ */ React6.createElement("h2", { className: "text-2xl font-bold text-white flex items-center gap-3" }, /* @__PURE__ */ React6.createElement("span", { className: "text-3xl" }, "\u{1F4DC}"), /* @__PURE__ */ React6.createElement("span", { style: { textShadow: "0 2px 12px rgba(255, 255, 255, 0.3)" } }, "\u5BF9\u8BDD\u5386\u53F2")),
2501
- /* @__PURE__ */ React6.createElement(
4009
+ /* @__PURE__ */ React21.createElement("h2", { className: "text-2xl font-bold text-white flex items-center gap-3" }, /* @__PURE__ */ React21.createElement("span", { className: "text-3xl" }, "\u{1F4DC}"), /* @__PURE__ */ React21.createElement("span", { style: { textShadow: "0 2px 12px rgba(255, 255, 255, 0.3)" } }, "\u5BF9\u8BDD\u5386\u53F2")),
4010
+ /* @__PURE__ */ React21.createElement(
2502
4011
  "button",
2503
4012
  {
2504
4013
  onClick: onClose,
@@ -2512,9 +4021,9 @@ var HistoryPanel = ({
2512
4021
  },
2513
4022
  "aria-label": "\u5173\u95ED"
2514
4023
  },
2515
- /* @__PURE__ */ React6.createElement("svg", { className: "w-6 h-6", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor" }, /* @__PURE__ */ React6.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }))
4024
+ /* @__PURE__ */ React21.createElement("svg", { className: "w-6 h-6", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor" }, /* @__PURE__ */ React21.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }))
2516
4025
  )
2517
- ), /* @__PURE__ */ React6.createElement("div", { className: "flex-1 overflow-y-auto p-6 space-y-4" }, history.length === 0 ? /* @__PURE__ */ React6.createElement("div", { className: "text-center text-white/70 py-20 text-lg font-medium", style: { textShadow: "0 2px 8px rgba(0, 0, 0, 0.3)" } }, "\u6682\u65E0\u5BF9\u8BDD\u5386\u53F2") : history.map((item, index) => /* @__PURE__ */ React6.createElement(
4026
+ ), /* @__PURE__ */ React21.createElement("div", { className: "flex-1 overflow-y-auto p-6 space-y-4" }, history.length === 0 ? /* @__PURE__ */ React21.createElement("div", { className: "text-center text-white/70 py-20 text-lg font-medium", style: { textShadow: "0 2px 8px rgba(0, 0, 0, 0.3)" } }, "\u6682\u65E0\u5BF9\u8BDD\u5386\u53F2") : history.map((item, index) => /* @__PURE__ */ React21.createElement(
2518
4027
  "div",
2519
4028
  {
2520
4029
  key: `${item.nodeIndex}-${item.dialogueIndex}-${index}`,
@@ -2527,7 +4036,7 @@ var HistoryPanel = ({
2527
4036
  boxShadow: "0 8px 24px rgba(255, 255, 255, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.25)"
2528
4037
  }
2529
4038
  },
2530
- /* @__PURE__ */ React6.createElement(
4039
+ /* @__PURE__ */ React21.createElement(
2531
4040
  "div",
2532
4041
  {
2533
4042
  className: "absolute top-0 left-0 right-0 h-0.5",
@@ -2536,7 +4045,7 @@ var HistoryPanel = ({
2536
4045
  }
2537
4046
  }
2538
4047
  ),
2539
- item.speaker && /* @__PURE__ */ React6.createElement(
4048
+ item.speaker && /* @__PURE__ */ React21.createElement(
2540
4049
  "div",
2541
4050
  {
2542
4051
  className: "inline-block px-5 py-2 rounded-xl mb-3 text-sm font-bold shadow-lg relative overflow-hidden",
@@ -2549,7 +4058,7 @@ var HistoryPanel = ({
2549
4058
  border: "1px solid rgba(255, 255, 255, 0.25)"
2550
4059
  }
2551
4060
  },
2552
- /* @__PURE__ */ React6.createElement(
4061
+ /* @__PURE__ */ React21.createElement(
2553
4062
  "div",
2554
4063
  {
2555
4064
  className: "absolute inset-0 opacity-30",
@@ -2560,9 +4069,9 @@ var HistoryPanel = ({
2560
4069
  }
2561
4070
  }
2562
4071
  ),
2563
- /* @__PURE__ */ React6.createElement("span", { className: "relative z-10 drop-shadow-lg" }, item.speaker)
4072
+ /* @__PURE__ */ React21.createElement("span", { className: "relative z-10 drop-shadow-lg" }, item.speaker)
2564
4073
  ),
2565
- /* @__PURE__ */ React6.createElement(
4074
+ /* @__PURE__ */ React21.createElement(
2566
4075
  "p",
2567
4076
  {
2568
4077
  className: "text-base leading-relaxed",
@@ -2573,7 +4082,7 @@ var HistoryPanel = ({
2573
4082
  },
2574
4083
  item.text
2575
4084
  )
2576
- ))), /* @__PURE__ */ React6.createElement(
4085
+ ))), /* @__PURE__ */ React21.createElement(
2577
4086
  "div",
2578
4087
  {
2579
4088
  className: "px-8 py-4 border-t text-center",
@@ -2585,7 +4094,7 @@ var HistoryPanel = ({
2585
4094
  boxShadow: "inset 0 1px 0 rgba(255, 255, 255, 0.2)"
2586
4095
  }
2587
4096
  },
2588
- /* @__PURE__ */ React6.createElement(
4097
+ /* @__PURE__ */ React21.createElement(
2589
4098
  "span",
2590
4099
  {
2591
4100
  className: "text-sm px-5 py-2.5 rounded-full inline-block font-medium",
@@ -2635,7 +4144,7 @@ var LoadingScreen = ({
2635
4144
  console.log("[LoadingScreen] Not showing, returning null");
2636
4145
  return null;
2637
4146
  }
2638
- const content = /* @__PURE__ */ React6.createElement(
4147
+ const content = /* @__PURE__ */ React21.createElement(
2639
4148
  "div",
2640
4149
  {
2641
4150
  className: `fixed inset-0 w-screen h-screen flex items-center justify-center ${className}`,
@@ -2647,7 +4156,7 @@ var LoadingScreen = ({
2647
4156
  padding: 0
2648
4157
  }
2649
4158
  },
2650
- /* @__PURE__ */ React6.createElement(
4159
+ /* @__PURE__ */ React21.createElement(
2651
4160
  "div",
2652
4161
  {
2653
4162
  className: "absolute inset-0 w-full h-full pointer-events-none",
@@ -2657,7 +4166,7 @@ var LoadingScreen = ({
2657
4166
  WebkitBackdropFilter: "blur(32px) saturate(200%)"
2658
4167
  }
2659
4168
  },
2660
- /* @__PURE__ */ React6.createElement(
4169
+ /* @__PURE__ */ React21.createElement(
2661
4170
  "div",
2662
4171
  {
2663
4172
  className: "flex items-center justify-center flex-col inset-0 w-full h-full pointer-events-none",
@@ -2672,7 +4181,7 @@ var LoadingScreen = ({
2672
4181
  animation: "gradientShift 15s ease infinite"
2673
4182
  }
2674
4183
  },
2675
- /* @__PURE__ */ React6.createElement(
4184
+ /* @__PURE__ */ React21.createElement(
2676
4185
  "div",
2677
4186
  {
2678
4187
  className: "text-lg font-medium text-white text-center px-6 py-3 rounded-2xl"
@@ -2707,13 +4216,13 @@ var LoadingScreen = ({
2707
4216
  LoadingScreen.displayName = "LoadingScreen";
2708
4217
  var VNModal = ({ title, show, onClose, children }) => {
2709
4218
  if (!show) return null;
2710
- return /* @__PURE__ */ React6.createElement(
4219
+ return /* @__PURE__ */ React21.createElement(
2711
4220
  "div",
2712
4221
  {
2713
4222
  className: "fixed inset-0 flex items-center justify-center bg-black/40 backdrop-blur-xl z-[1000000] pointer-events-auto transition-all animate-in fade-in zoom-in-95 duration-300",
2714
4223
  onClick: onClose
2715
4224
  },
2716
- /* @__PURE__ */ React6.createElement(
4225
+ /* @__PURE__ */ React21.createElement(
2717
4226
  "div",
2718
4227
  {
2719
4228
  className: "w-full max-w-lg mx-4 p-8 rounded-[2.5rem] border border-white/40 shadow-[0_20px_80px_rgba(0,0,0,0.4)] relative overflow-hidden",
@@ -2724,9 +4233,9 @@ var VNModal = ({ title, show, onClose, children }) => {
2724
4233
  },
2725
4234
  onClick: (e) => e.stopPropagation()
2726
4235
  },
2727
- /* @__PURE__ */ React6.createElement("div", { className: "absolute -top-24 -right-24 w-64 h-64 bg-cyan-400/20 rounded-full blur-3xl pointer-events-none" }),
2728
- /* @__PURE__ */ React6.createElement("div", { className: "absolute -bottom-24 -left-24 w-64 h-64 bg-pink-400/20 rounded-full blur-3xl pointer-events-none" }),
2729
- /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between mb-8 relative" }, /* @__PURE__ */ React6.createElement("div", { className: "flex flex-col" }, /* @__PURE__ */ React6.createElement("h2", { className: "text-2xl font-bold text-white tracking-wider drop-shadow-lg" }, title), /* @__PURE__ */ React6.createElement("div", { className: "h-1.5 w-16 bg-white/60 rounded-full mt-2 shadow-[0_0_10px_rgba(255,255,255,0.5)]" })), /* @__PURE__ */ React6.createElement(
4236
+ /* @__PURE__ */ React21.createElement("div", { className: "absolute -top-24 -right-24 w-64 h-64 bg-cyan-400/20 rounded-full blur-3xl pointer-events-none" }),
4237
+ /* @__PURE__ */ React21.createElement("div", { className: "absolute -bottom-24 -left-24 w-64 h-64 bg-pink-400/20 rounded-full blur-3xl pointer-events-none" }),
4238
+ /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between mb-8 relative" }, /* @__PURE__ */ React21.createElement("div", { className: "flex flex-col" }, /* @__PURE__ */ React21.createElement("h2", { className: "text-2xl font-bold text-white tracking-wider drop-shadow-lg" }, title), /* @__PURE__ */ React21.createElement("div", { className: "h-1.5 w-16 bg-white/60 rounded-full mt-2 shadow-[0_0_10px_rgba(255,255,255,0.5)]" })), /* @__PURE__ */ React21.createElement(
2730
4239
  "button",
2731
4240
  {
2732
4241
  onClick: onClose,
@@ -2734,8 +4243,8 @@ var VNModal = ({ title, show, onClose, children }) => {
2734
4243
  },
2735
4244
  "\u2715"
2736
4245
  )),
2737
- /* @__PURE__ */ React6.createElement("div", { className: "text-white leading-relaxed max-h-[50vh] overflow-y-auto pr-4 custom-scrollbar relative font-medium" }, children),
2738
- /* @__PURE__ */ React6.createElement("div", { className: "mt-10 flex justify-center relative" }, /* @__PURE__ */ React6.createElement(
4246
+ /* @__PURE__ */ React21.createElement("div", { className: "text-white leading-relaxed max-h-[50vh] overflow-y-auto pr-4 custom-scrollbar relative font-medium" }, children),
4247
+ /* @__PURE__ */ React21.createElement("div", { className: "mt-10 flex justify-center relative" }, /* @__PURE__ */ React21.createElement(
2739
4248
  "button",
2740
4249
  {
2741
4250
  onClick: onClose,
@@ -2763,7 +4272,7 @@ var StartScreen = ({
2763
4272
  }, []);
2764
4273
  if (!isMounted) return null;
2765
4274
  if (!showStartScreen) return null;
2766
- const content = /* @__PURE__ */ React6.createElement(
4275
+ const content = /* @__PURE__ */ React21.createElement(
2767
4276
  "div",
2768
4277
  {
2769
4278
  className: `fixed inset-0 w-screen h-screen flex items-center justify-center overflow-hidden ${className}`,
@@ -2775,7 +4284,7 @@ var StartScreen = ({
2775
4284
  padding: 0
2776
4285
  }
2777
4286
  },
2778
- /* @__PURE__ */ React6.createElement("div", { className: "absolute inset-0 w-full h-full overflow-hidden" }, /* @__PURE__ */ React6.createElement(
4287
+ /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 w-full h-full overflow-hidden" }, /* @__PURE__ */ React21.createElement(
2779
4288
  "div",
2780
4289
  {
2781
4290
  className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[150vw] h-[150vw] opacity-40 pointer-events-none",
@@ -2785,7 +4294,7 @@ var StartScreen = ({
2785
4294
  animation: "pulse 10s ease-in-out infinite"
2786
4295
  }
2787
4296
  }
2788
- ), /* @__PURE__ */ React6.createElement("div", { className: "absolute inset-0 transition-opacity duration-1000" }, /* @__PURE__ */ React6.createElement("div", { className: "absolute top-[-10%] left-[-10%] w-[60%] h-[60%] bg-cyan-500/10 rounded-full blur-[120px] animate-blob" }), /* @__PURE__ */ React6.createElement("div", { className: "absolute bottom-[-10%] right-[-10%] w-[60%] h-[60%] bg-purple-500/10 rounded-full blur-[120px] animate-blob animation-delay-2000" }), /* @__PURE__ */ React6.createElement("div", { className: "absolute top-[20%] right-[10%] w-[40%] h-[40%] bg-pink-500/10 rounded-full blur-[100px] animate-blob animation-delay-4000" })), /* @__PURE__ */ React6.createElement("div", { className: "absolute inset-0 opacity-60" }, [...Array(40)].map((_, i) => /* @__PURE__ */ React6.createElement(
4297
+ ), /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 transition-opacity duration-1000" }, /* @__PURE__ */ React21.createElement("div", { className: "absolute top-[-10%] left-[-10%] w-[60%] h-[60%] bg-cyan-500/10 rounded-full blur-[120px] animate-blob" }), /* @__PURE__ */ React21.createElement("div", { className: "absolute bottom-[-10%] right-[-10%] w-[60%] h-[60%] bg-purple-500/10 rounded-full blur-[120px] animate-blob animation-delay-2000" }), /* @__PURE__ */ React21.createElement("div", { className: "absolute top-[20%] right-[10%] w-[40%] h-[40%] bg-pink-500/10 rounded-full blur-[100px] animate-blob animation-delay-4000" })), /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 opacity-60" }, [...Array(40)].map((_, i) => /* @__PURE__ */ React21.createElement(
2789
4298
  "div",
2790
4299
  {
2791
4300
  key: i,
@@ -2801,7 +4310,7 @@ var StartScreen = ({
2801
4310
  }
2802
4311
  }
2803
4312
  )))),
2804
- /* @__PURE__ */ React6.createElement("div", { className: "relative z-10 w-full max-w-5xl mx-auto px-6 flex flex-col items-center" }, /* @__PURE__ */ React6.createElement("div", { className: "text-center mb-20 md:mb-32 group flex flex-col items-center" }, /* @__PURE__ */ React6.createElement("div", { className: "w-40 h-px bg-gradient-to-r from-transparent via-white/60 to-transparent mb-10 shadow-[0_0_15px_rgba(255,255,255,0.5)]" }), /* @__PURE__ */ React6.createElement(
4313
+ /* @__PURE__ */ React21.createElement("div", { className: "relative z-10 w-full max-w-5xl mx-auto px-6 flex flex-col items-center" }, /* @__PURE__ */ React21.createElement("div", { className: "text-center mb-20 md:mb-32 group flex flex-col items-center" }, /* @__PURE__ */ React21.createElement("div", { className: "w-40 h-px bg-gradient-to-r from-transparent via-white/60 to-transparent mb-10 shadow-[0_0_15px_rgba(255,255,255,0.5)]" }), /* @__PURE__ */ React21.createElement(
2805
4314
  "h1",
2806
4315
  {
2807
4316
  className: "text-6xl md:text-8xl font-black text-white tracking-[0.15em] leading-tight select-none",
@@ -2811,49 +4320,49 @@ var StartScreen = ({
2811
4320
  }
2812
4321
  },
2813
4322
  scriptName || "VISUAL NOVEL"
2814
- ), /* @__PURE__ */ React6.createElement("div", { className: "mt-10 flex flex-col items-center gap-4" }, /* @__PURE__ */ React6.createElement("div", { className: "h-1 w-32 bg-white rounded-full opacity-80 shadow-[0_0_20px_rgba(255,255,255,0.8)]" }), /* @__PURE__ */ React6.createElement("span", { className: "text-sm md:text-base tracking-[0.8em] text-white/60 font-medium uppercase translate-x-[0.4em]" }, "Adventure System"))), /* @__PURE__ */ React6.createElement("div", { className: "flex flex-col gap-10 items-center w-full max-w-sm" }, /* @__PURE__ */ React6.createElement(
4323
+ ), /* @__PURE__ */ React21.createElement("div", { className: "mt-10 flex flex-col items-center gap-4" }, /* @__PURE__ */ React21.createElement("div", { className: "h-1 w-32 bg-white rounded-full opacity-80 shadow-[0_0_20px_rgba(255,255,255,0.8)]" }), /* @__PURE__ */ React21.createElement("span", { className: "text-sm md:text-base tracking-[0.8em] text-white/60 font-medium uppercase translate-x-[0.4em]" }, "Adventure System"))), /* @__PURE__ */ React21.createElement("div", { className: "flex flex-col gap-10 items-center w-full max-w-sm" }, /* @__PURE__ */ React21.createElement(
2815
4324
  "button",
2816
4325
  {
2817
4326
  onClick: onStart,
2818
4327
  className: "group relative w-full h-20 flex items-center justify-center transition-all duration-500 active:scale-95"
2819
4328
  },
2820
- /* @__PURE__ */ React6.createElement(
4329
+ /* @__PURE__ */ React21.createElement(
2821
4330
  "div",
2822
4331
  {
2823
4332
  className: "absolute inset-0 bg-white/10 backdrop-blur-2xl border-2 border-white/40 rounded-3xl transition-all duration-500 group-hover:bg-white/25 group-hover:border-white/70 group-hover:shadow-[0_0_50px_rgba(255,255,255,0.3)] group-hover:-inset-1"
2824
4333
  }
2825
4334
  ),
2826
- /* @__PURE__ */ React6.createElement("div", { className: "absolute inset-0 overflow-hidden rounded-3xl pointer-events-none" }, /* @__PURE__ */ React6.createElement("div", { className: "absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-700 bg-gradient-to-r from-transparent via-white/20 to-transparent -skew-x-[25deg] -translate-x-[200%] group-hover:translate-x-[200%] transition-transform duration-1000 ease-in-out" })),
2827
- /* @__PURE__ */ React6.createElement("div", { className: "relative flex items-center gap-6" }, /* @__PURE__ */ React6.createElement("div", { className: "w-3 h-3 rounded-full bg-cyan-400 animate-ping opacity-75" }), /* @__PURE__ */ React6.createElement("span", { className: "text-2xl font-black text-white tracking-[0.4em] drop-shadow-md" }, startText), /* @__PURE__ */ React6.createElement("div", { className: "w-3 h-3 rounded-full bg-pink-400 animate-ping opacity-75" }))
2828
- ), /* @__PURE__ */ React6.createElement("div", { className: "flex gap-8 w-full justify-center" }, [
4335
+ /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 overflow-hidden rounded-3xl pointer-events-none" }, /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-700 bg-gradient-to-r from-transparent via-white/20 to-transparent -skew-x-[25deg] -translate-x-[200%] group-hover:translate-x-[200%] transition-transform duration-1000 ease-in-out" })),
4336
+ /* @__PURE__ */ React21.createElement("div", { className: "relative flex items-center gap-6" }, /* @__PURE__ */ React21.createElement("div", { className: "w-3 h-3 rounded-full bg-cyan-400 animate-ping opacity-75" }), /* @__PURE__ */ React21.createElement("span", { className: "text-2xl font-black text-white tracking-[0.4em] drop-shadow-md" }, startText), /* @__PURE__ */ React21.createElement("div", { className: "w-3 h-3 rounded-full bg-pink-400 animate-ping opacity-75" }))
4337
+ ), /* @__PURE__ */ React21.createElement("div", { className: "flex gap-8 w-full justify-center" }, [
2829
4338
  { text: settingsText, onClick: () => setShowSettings(true), color: "rgba(57, 197, 187, 0.2)" },
2830
4339
  { text: aboutText, onClick: () => setShowAbout(true), color: "rgba(255, 182, 193, 0.2)" }
2831
- ].map((btn, idx) => /* @__PURE__ */ React6.createElement(
4340
+ ].map((btn, idx) => /* @__PURE__ */ React21.createElement(
2832
4341
  "button",
2833
4342
  {
2834
4343
  key: idx,
2835
4344
  onClick: btn.onClick,
2836
4345
  className: "group relative flex-1 h-14 flex items-center justify-center transition-all duration-300 active:scale-95 overflow-hidden rounded-2xl"
2837
4346
  },
2838
- /* @__PURE__ */ React6.createElement(
4347
+ /* @__PURE__ */ React21.createElement(
2839
4348
  "div",
2840
4349
  {
2841
4350
  className: "absolute inset-0 bg-white/5 backdrop-blur-xl border border-white/20 transition-all duration-300 group-hover:bg-white/15 group-hover:border-white/40"
2842
4351
  }
2843
4352
  ),
2844
- /* @__PURE__ */ React6.createElement(
4353
+ /* @__PURE__ */ React21.createElement(
2845
4354
  "div",
2846
4355
  {
2847
4356
  className: "absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity",
2848
4357
  style: { background: btn.color }
2849
4358
  }
2850
4359
  ),
2851
- /* @__PURE__ */ React6.createElement("span", { className: "relative text-sm md:text-base font-bold text-white/80 group-hover:text-white tracking-[0.25em] transition-colors uppercase" }, btn.text)
4360
+ /* @__PURE__ */ React21.createElement("span", { className: "relative text-sm md:text-base font-bold text-white/80 group-hover:text-white tracking-[0.25em] transition-colors uppercase" }, btn.text)
2852
4361
  ))))),
2853
- /* @__PURE__ */ React6.createElement("div", { className: "fixed bottom-10 left-0 right-0 text-center pointer-events-none select-none" }, /* @__PURE__ */ React6.createElement("div", { className: "inline-block px-6 py-2 rounded-full bg-white/5 backdrop-blur-md border border-white/10" }, /* @__PURE__ */ React6.createElement("span", { className: "text-[10px] md:text-xs text-white/30 tracking-[0.5em] font-light uppercase" }, "Ver 1.6.2 \u2014 ENGINE POWERED BY SA2KIT"))),
2854
- /* @__PURE__ */ React6.createElement(VNModal, { title: settingsText, show: showSettings, onClose: () => setShowSettings(false) }, /* @__PURE__ */ React6.createElement("div", { className: "space-y-8 py-4" }, /* @__PURE__ */ React6.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React6.createElement("div", { className: "flex justify-between items-center text-sm font-bold tracking-widest text-white/60" }, /* @__PURE__ */ React6.createElement("span", null, "MUSIC VOLUME"), /* @__PURE__ */ React6.createElement("span", null, "80%")), /* @__PURE__ */ React6.createElement("div", { className: "h-3 bg-white/10 rounded-full p-0.5 border border-white/10" }, /* @__PURE__ */ React6.createElement("div", { className: "h-full w-[80%] bg-gradient-to-r from-cyan-400 to-blue-500 rounded-full shadow-[0_0_15px_rgba(34,211,238,0.5)]" }))), /* @__PURE__ */ React6.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React6.createElement("div", { className: "flex justify-between items-center text-sm font-bold tracking-widest text-white/60" }, /* @__PURE__ */ React6.createElement("span", null, "TEXT SPEED"), /* @__PURE__ */ React6.createElement("span", null, "NORMAL")), /* @__PURE__ */ React6.createElement("div", { className: "flex gap-3" }, ["SLOW", "NORMAL", "FAST"].map((s, i) => /* @__PURE__ */ React6.createElement("div", { key: s, className: `flex-1 py-3 rounded-xl border text-center text-xs font-bold transition-all cursor-pointer ${i === 1 ? "bg-white/20 border-white/60 text-white" : "bg-white/5 border-white/10 text-white/40 hover:bg-white/10 hover:border-white/30"}` }, s)))), /* @__PURE__ */ React6.createElement("div", { className: "pt-4 flex items-center justify-between opacity-50 italic text-xs border-t border-white/10" }, /* @__PURE__ */ React6.createElement("span", null, "Auto Save Enabled"), /* @__PURE__ */ React6.createElement("span", null, "Cloud Sync Active")))),
2855
- /* @__PURE__ */ React6.createElement(VNModal, { title: aboutText, show: showAbout, onClose: () => setShowAbout(false) }, /* @__PURE__ */ React6.createElement("div", { className: "space-y-8 py-4" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center gap-6 p-6 rounded-3xl bg-white/5 border border-white/10" }, /* @__PURE__ */ React6.createElement("div", { className: "w-20 h-20 rounded-2xl bg-gradient-to-br from-cyan-400 via-blue-500 to-purple-600 flex items-center justify-center text-3xl font-black text-white shadow-[0_10px_30px_rgba(0,0,0,0.3)]" }, "S2"), /* @__PURE__ */ React6.createElement("div", null, /* @__PURE__ */ React6.createElement("h3", { className: "text-2xl font-black text-white tracking-tight" }, scriptName || "Project SA2"), /* @__PURE__ */ React6.createElement("p", { className: "text-xs font-bold text-white/40 tracking-widest mt-1 uppercase" }, "Visual Novel Experience"))), /* @__PURE__ */ React6.createElement("div", { className: "space-y-4 px-2" }, /* @__PURE__ */ React6.createElement("p", { className: "text-white/80 font-medium leading-relaxed" }, "\u91C7\u7528 sa2kit \u5F15\u64CE\u6784\u5EFA\u7684\u65B0\u4E00\u4EE3\u5B9E\u65F6 3D \u89C6\u89C9\u5C0F\u8BF4\u3002\u7ED3\u5408\u4E86 MMD \u5B9E\u65F6\u6E32\u67D3\u6280\u672F\u4E0E\u4EA4\u4E92\u5F0F\u5267\u60C5\u5206\u652F\u7CFB\u7EDF\uFF0C\u81F4\u529B\u4E8E\u6253\u9020\u6781\u81F4\u7684\u6C89\u6D78\u5F0F\u53D9\u4E8B\u4F53\u9A8C\u3002")), /* @__PURE__ */ React6.createElement("div", { className: "grid grid-cols-2 gap-4 pt-6 border-t border-white/10" }, /* @__PURE__ */ React6.createElement("div", { className: "flex flex-col gap-1" }, /* @__PURE__ */ React6.createElement("span", { className: "text-[10px] font-bold text-white/30 tracking-widest" }, "DEVELOPER"), /* @__PURE__ */ React6.createElement("span", { className: "text-xs font-bold text-white/80" }, "SA2KIT TEAM")), /* @__PURE__ */ React6.createElement("div", { className: "flex flex-col gap-1 text-right" }, /* @__PURE__ */ React6.createElement("span", { className: "text-[10px] font-bold text-white/30 tracking-widest" }, "ENGINE"), /* @__PURE__ */ React6.createElement("span", { className: "text-xs font-bold text-white/80" }, "THREE.JS / REACT"))))),
2856
- /* @__PURE__ */ React6.createElement("style", null, `
4362
+ /* @__PURE__ */ React21.createElement("div", { className: "fixed bottom-10 left-0 right-0 text-center pointer-events-none select-none" }, /* @__PURE__ */ React21.createElement("div", { className: "inline-block px-6 py-2 rounded-full bg-white/5 backdrop-blur-md border border-white/10" }, /* @__PURE__ */ React21.createElement("span", { className: "text-[10px] md:text-xs text-white/30 tracking-[0.5em] font-light uppercase" }, "Ver 1.6.2 \u2014 ENGINE POWERED BY SA2KIT"))),
4363
+ /* @__PURE__ */ React21.createElement(VNModal, { title: settingsText, show: showSettings, onClose: () => setShowSettings(false) }, /* @__PURE__ */ React21.createElement("div", { className: "space-y-8 py-4" }, /* @__PURE__ */ React21.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React21.createElement("div", { className: "flex justify-between items-center text-sm font-bold tracking-widest text-white/60" }, /* @__PURE__ */ React21.createElement("span", null, "MUSIC VOLUME"), /* @__PURE__ */ React21.createElement("span", null, "80%")), /* @__PURE__ */ React21.createElement("div", { className: "h-3 bg-white/10 rounded-full p-0.5 border border-white/10" }, /* @__PURE__ */ React21.createElement("div", { className: "h-full w-[80%] bg-gradient-to-r from-cyan-400 to-blue-500 rounded-full shadow-[0_0_15px_rgba(34,211,238,0.5)]" }))), /* @__PURE__ */ React21.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React21.createElement("div", { className: "flex justify-between items-center text-sm font-bold tracking-widest text-white/60" }, /* @__PURE__ */ React21.createElement("span", null, "TEXT SPEED"), /* @__PURE__ */ React21.createElement("span", null, "NORMAL")), /* @__PURE__ */ React21.createElement("div", { className: "flex gap-3" }, ["SLOW", "NORMAL", "FAST"].map((s, i) => /* @__PURE__ */ React21.createElement("div", { key: s, className: `flex-1 py-3 rounded-xl border text-center text-xs font-bold transition-all cursor-pointer ${i === 1 ? "bg-white/20 border-white/60 text-white" : "bg-white/5 border-white/10 text-white/40 hover:bg-white/10 hover:border-white/30"}` }, s)))), /* @__PURE__ */ React21.createElement("div", { className: "pt-4 flex items-center justify-between opacity-50 italic text-xs border-t border-white/10" }, /* @__PURE__ */ React21.createElement("span", null, "Auto Save Enabled"), /* @__PURE__ */ React21.createElement("span", null, "Cloud Sync Active")))),
4364
+ /* @__PURE__ */ React21.createElement(VNModal, { title: aboutText, show: showAbout, onClose: () => setShowAbout(false) }, /* @__PURE__ */ React21.createElement("div", { className: "space-y-8 py-4" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center gap-6 p-6 rounded-3xl bg-white/5 border border-white/10" }, /* @__PURE__ */ React21.createElement("div", { className: "w-20 h-20 rounded-2xl bg-gradient-to-br from-cyan-400 via-blue-500 to-purple-600 flex items-center justify-center text-3xl font-black text-white shadow-[0_10px_30px_rgba(0,0,0,0.3)]" }, "S2"), /* @__PURE__ */ React21.createElement("div", null, /* @__PURE__ */ React21.createElement("h3", { className: "text-2xl font-black text-white tracking-tight" }, scriptName || "Project SA2"), /* @__PURE__ */ React21.createElement("p", { className: "text-xs font-bold text-white/40 tracking-widest mt-1 uppercase" }, "Visual Novel Experience"))), /* @__PURE__ */ React21.createElement("div", { className: "space-y-4 px-2" }, /* @__PURE__ */ React21.createElement("p", { className: "text-white/80 font-medium leading-relaxed" }, "\u91C7\u7528 sa2kit \u5F15\u64CE\u6784\u5EFA\u7684\u65B0\u4E00\u4EE3\u5B9E\u65F6 3D \u89C6\u89C9\u5C0F\u8BF4\u3002\u7ED3\u5408\u4E86 MMD \u5B9E\u65F6\u6E32\u67D3\u6280\u672F\u4E0E\u4EA4\u4E92\u5F0F\u5267\u60C5\u5206\u652F\u7CFB\u7EDF\uFF0C\u81F4\u529B\u4E8E\u6253\u9020\u6781\u81F4\u7684\u6C89\u6D78\u5F0F\u53D9\u4E8B\u4F53\u9A8C\u3002")), /* @__PURE__ */ React21.createElement("div", { className: "grid grid-cols-2 gap-4 pt-6 border-t border-white/10" }, /* @__PURE__ */ React21.createElement("div", { className: "flex flex-col gap-1" }, /* @__PURE__ */ React21.createElement("span", { className: "text-[10px] font-bold text-white/30 tracking-widest" }, "DEVELOPER"), /* @__PURE__ */ React21.createElement("span", { className: "text-xs font-bold text-white/80" }, "SA2KIT TEAM")), /* @__PURE__ */ React21.createElement("div", { className: "flex flex-col gap-1 text-right" }, /* @__PURE__ */ React21.createElement("span", { className: "text-[10px] font-bold text-white/30 tracking-widest" }, "ENGINE"), /* @__PURE__ */ React21.createElement("span", { className: "text-xs font-bold text-white/80" }, "THREE.JS / REACT"))))),
4365
+ /* @__PURE__ */ React21.createElement("style", null, `
2857
4366
  @keyframes floatParticle {
2858
4367
  from { transform: translateY(0) translateX(0); opacity: 0; }
2859
4368
  20% { opacity: 1; }
@@ -2938,14 +4447,14 @@ var LoadingOverlay = ({
2938
4447
  onStart,
2939
4448
  className = ""
2940
4449
  }) => {
2941
- return /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(
4450
+ return /* @__PURE__ */ React21.createElement(React21.Fragment, null, /* @__PURE__ */ React21.createElement(
2942
4451
  LoadingScreen,
2943
4452
  {
2944
4453
  isLoading,
2945
4454
  loadingText,
2946
4455
  className
2947
4456
  }
2948
- ), /* @__PURE__ */ React6.createElement(
4457
+ ), /* @__PURE__ */ React21.createElement(
2949
4458
  StartScreen,
2950
4459
  {
2951
4460
  showStartScreen,
@@ -2963,18 +4472,18 @@ var SkipConfirmDialog = ({
2963
4472
  onConfirm,
2964
4473
  onCancel
2965
4474
  }) => {
2966
- const [isMounted, setIsMounted] = React6.useState(false);
2967
- React6.useEffect(() => {
4475
+ const [isMounted, setIsMounted] = React21.useState(false);
4476
+ React21.useEffect(() => {
2968
4477
  setIsMounted(true);
2969
4478
  }, []);
2970
4479
  if (!isMounted) return null;
2971
- const content = /* @__PURE__ */ React6.createElement(
4480
+ const content = /* @__PURE__ */ React21.createElement(
2972
4481
  "div",
2973
4482
  {
2974
4483
  className: "fixed inset-0 flex items-center justify-center bg-black/40 backdrop-blur-sm pointer-events-auto",
2975
4484
  style: { zIndex: 999999 }
2976
4485
  },
2977
- /* @__PURE__ */ React6.createElement(
4486
+ /* @__PURE__ */ React21.createElement(
2978
4487
  "div",
2979
4488
  {
2980
4489
  className: "p-8 rounded-3xl border border-white/30 shadow-2xl max-w-sm w-full mx-4 overflow-hidden relative",
@@ -2986,16 +4495,16 @@ var SkipConfirmDialog = ({
2986
4495
  WebkitBackdropFilter: "blur(32px) saturate(200%)"
2987
4496
  }
2988
4497
  },
2989
- /* @__PURE__ */ React6.createElement("h3", { className: "text-xl font-bold text-white mb-4 drop-shadow-md" }, "\u52A8\u753B\u5C1A\u672A\u64AD\u653E\u5B8C\u6210"),
2990
- /* @__PURE__ */ React6.createElement("p", { className: "text-white/90 mb-8 leading-relaxed drop-shadow-sm" }, "\u5F53\u524D\u573A\u666F\u7684\u52A8\u753B\u8FD8\u6CA1\u6709\u5B8C\u6574\u64AD\u653E\u4E00\u904D\uFF0C\u662F\u5426\u76F4\u63A5\u8DF3\u8F6C\u5230\u4E0B\u4E00\u4E2A\u573A\u666F\uFF1F"),
2991
- /* @__PURE__ */ React6.createElement("div", { className: "flex justify-end gap-4" }, /* @__PURE__ */ React6.createElement(
4498
+ /* @__PURE__ */ React21.createElement("h3", { className: "text-xl font-bold text-white mb-4 drop-shadow-md" }, "\u52A8\u753B\u5C1A\u672A\u64AD\u653E\u5B8C\u6210"),
4499
+ /* @__PURE__ */ React21.createElement("p", { className: "text-white/90 mb-8 leading-relaxed drop-shadow-sm" }, "\u5F53\u524D\u573A\u666F\u7684\u52A8\u753B\u8FD8\u6CA1\u6709\u5B8C\u6574\u64AD\u653E\u4E00\u904D\uFF0C\u662F\u5426\u76F4\u63A5\u8DF3\u8F6C\u5230\u4E0B\u4E00\u4E2A\u573A\u666F\uFF1F"),
4500
+ /* @__PURE__ */ React21.createElement("div", { className: "flex justify-end gap-4" }, /* @__PURE__ */ React21.createElement(
2992
4501
  "button",
2993
4502
  {
2994
4503
  onClick: onCancel,
2995
4504
  className: "px-6 py-2.5 rounded-2xl text-white/80 font-medium hover:text-white hover:bg-white/10 transition-all border border-white/10 hover:border-white/30"
2996
4505
  },
2997
4506
  "\u53D6\u6D88"
2998
- ), /* @__PURE__ */ React6.createElement(
4507
+ ), /* @__PURE__ */ React21.createElement(
2999
4508
  "button",
3000
4509
  {
3001
4510
  onClick: onConfirm,
@@ -3019,18 +4528,18 @@ var ChoiceMenu = ({
3019
4528
  onSelect,
3020
4529
  theme
3021
4530
  }) => {
3022
- const [isMounted, setIsMounted] = React6.useState(false);
3023
- React6.useEffect(() => {
4531
+ const [isMounted, setIsMounted] = React21.useState(false);
4532
+ React21.useEffect(() => {
3024
4533
  setIsMounted(true);
3025
4534
  }, []);
3026
4535
  if (!isMounted) return null;
3027
- const content = /* @__PURE__ */ React6.createElement(
4536
+ const content = /* @__PURE__ */ React21.createElement(
3028
4537
  "div",
3029
4538
  {
3030
4539
  className: "fixed inset-0 flex flex-col items-center justify-center bg-black/20 backdrop-blur-sm pointer-events-auto transition-all animate-in fade-in duration-500",
3031
4540
  style: { zIndex: 1e6 }
3032
4541
  },
3033
- /* @__PURE__ */ React6.createElement("div", { className: "flex flex-col gap-4 w-full max-w-md px-6" }, choices.map((choice, index) => /* @__PURE__ */ React6.createElement(
4542
+ /* @__PURE__ */ React21.createElement("div", { className: "flex flex-col gap-4 w-full max-w-md px-6" }, choices.map((choice, index) => /* @__PURE__ */ React21.createElement(
3034
4543
  "button",
3035
4544
  {
3036
4545
  key: index,
@@ -3044,9 +4553,9 @@ var ChoiceMenu = ({
3044
4553
  WebkitBackdropFilter: "blur(32px) saturate(200%)"
3045
4554
  }
3046
4555
  },
3047
- /* @__PURE__ */ React6.createElement("div", { className: "absolute inset-0 bg-white/10 opacity-0 group-hover:opacity-100 transition-opacity" }),
3048
- /* @__PURE__ */ React6.createElement("div", { className: "absolute inset-0 bg-gradient-to-r from-transparent via-white/5 to-transparent -translate-x-full group-hover:translate-x-full transition-transform duration-1000 ease-in-out" }),
3049
- /* @__PURE__ */ React6.createElement("span", { className: "relative z-10 drop-shadow-md" }, choice.text)
4556
+ /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 bg-white/10 opacity-0 group-hover:opacity-100 transition-opacity" }),
4557
+ /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 bg-gradient-to-r from-transparent via-white/5 to-transparent -translate-x-full group-hover:translate-x-full transition-transform duration-1000 ease-in-out" }),
4558
+ /* @__PURE__ */ React21.createElement("span", { className: "relative z-10 drop-shadow-md" }, choice.text)
3050
4559
  )))
3051
4560
  );
3052
4561
  let portalContainer = document.getElementById("dialogue-portal-root");
@@ -3328,16 +4837,16 @@ var MMDVisualNovel = forwardRef(
3328
4837
  };
3329
4838
  }, []);
3330
4839
  if (!currentNode) {
3331
- return /* @__PURE__ */ React6.createElement("div", { className: "flex h-full w-full items-center justify-center bg-black text-white" }, "\u5267\u672C\u4E3A\u7A7A");
4840
+ return /* @__PURE__ */ React21.createElement("div", { className: "flex h-full w-full items-center justify-center bg-black text-white" }, "\u5267\u672C\u4E3A\u7A7A");
3332
4841
  }
3333
- return /* @__PURE__ */ React6.createElement(
4842
+ return /* @__PURE__ */ React21.createElement(
3334
4843
  "div",
3335
4844
  {
3336
4845
  ref: containerRef,
3337
4846
  className: `relative bg-black ${className}`,
3338
4847
  style: { width: "100%", height: "100%", overflow: "hidden", ...style }
3339
4848
  },
3340
- /* @__PURE__ */ React6.createElement(
4849
+ /* @__PURE__ */ React21.createElement(
3341
4850
  "div",
3342
4851
  {
3343
4852
  className: "absolute inset-0 w-full h-full",
@@ -3348,7 +4857,7 @@ var MMDVisualNovel = forwardRef(
3348
4857
  transition: "opacity 0.3s ease-in-out"
3349
4858
  }
3350
4859
  },
3351
- !isTransitioning && /* @__PURE__ */ React6.createElement(
4860
+ !isTransitioning && /* @__PURE__ */ React21.createElement(
3352
4861
  MMDPlayerBase,
3353
4862
  {
3354
4863
  key: currentNode.id,
@@ -3397,13 +4906,13 @@ var MMDVisualNovel = forwardRef(
3397
4906
  }
3398
4907
  )
3399
4908
  ),
3400
- activeEffect && /* @__PURE__ */ React6.createElement(
4909
+ activeEffect && /* @__PURE__ */ React21.createElement(
3401
4910
  "div",
3402
4911
  {
3403
4912
  className: "pointer-events-none absolute inset-0 flex items-center justify-center",
3404
4913
  style: { zIndex: 999 }
3405
4914
  },
3406
- activeEffect.type === "flash" && /* @__PURE__ */ React6.createElement(
4915
+ activeEffect.type === "flash" && /* @__PURE__ */ React21.createElement(
3407
4916
  "div",
3408
4917
  {
3409
4918
  className: "h-full w-full",
@@ -3413,7 +4922,7 @@ var MMDVisualNovel = forwardRef(
3413
4922
  }
3414
4923
  }
3415
4924
  ),
3416
- activeEffect.type === "gif" && activeEffect.url && /* @__PURE__ */ React6.createElement(
4925
+ activeEffect.type === "gif" && activeEffect.url && /* @__PURE__ */ React21.createElement(
3417
4926
  "img",
3418
4927
  {
3419
4928
  src: activeEffect.url,
@@ -3421,7 +4930,7 @@ var MMDVisualNovel = forwardRef(
3421
4930
  className: activeEffect.position === "full" ? "h-full w-full object-cover" : "max-h-full max-w-full"
3422
4931
  }
3423
4932
  ),
3424
- /* @__PURE__ */ React6.createElement("style", null, `
4933
+ /* @__PURE__ */ React21.createElement("style", null, `
3425
4934
  @keyframes flash-anim {
3426
4935
  0% { opacity: 0; }
3427
4936
  25% { opacity: 1; }
@@ -3429,7 +4938,7 @@ var MMDVisualNovel = forwardRef(
3429
4938
  }
3430
4939
  `)
3431
4940
  ),
3432
- /* @__PURE__ */ React6.createElement(
4941
+ /* @__PURE__ */ React21.createElement(
3433
4942
  LoadingOverlay,
3434
4943
  {
3435
4944
  isLoading: (() => {
@@ -3463,7 +4972,7 @@ var MMDVisualNovel = forwardRef(
3463
4972
  shouldShow,
3464
4973
  dialogue: currentDialogue
3465
4974
  });
3466
- return shouldShow ? /* @__PURE__ */ React6.createElement(
4975
+ return shouldShow ? /* @__PURE__ */ React21.createElement(
3467
4976
  DialogueBox,
3468
4977
  {
3469
4978
  dialogue: currentDialogue,
@@ -3489,7 +4998,7 @@ var MMDVisualNovel = forwardRef(
3489
4998
  }
3490
4999
  ) : null;
3491
5000
  })(),
3492
- pendingNodeIndex !== null && /* @__PURE__ */ React6.createElement(
5001
+ pendingNodeIndex !== null && /* @__PURE__ */ React21.createElement(
3493
5002
  SkipConfirmDialog,
3494
5003
  {
3495
5004
  onConfirm: () => {
@@ -3502,7 +5011,7 @@ var MMDVisualNovel = forwardRef(
3502
5011
  }
3503
5012
  }
3504
5013
  ),
3505
- showChoices && (currentDialogue?.choices || currentNode.choices) && /* @__PURE__ */ React6.createElement(
5014
+ showChoices && (currentDialogue?.choices || currentNode.choices) && /* @__PURE__ */ React21.createElement(
3506
5015
  ChoiceMenu,
3507
5016
  {
3508
5017
  choices: currentDialogue?.choices || currentNode.choices,
@@ -3541,7 +5050,7 @@ var MMDVisualNovel = forwardRef(
3541
5050
  }
3542
5051
  }
3543
5052
  ),
3544
- showHistory && /* @__PURE__ */ React6.createElement(
5053
+ showHistory && /* @__PURE__ */ React21.createElement(
3545
5054
  HistoryPanel,
3546
5055
  {
3547
5056
  history,
@@ -3574,18 +5083,18 @@ var MusicControls = ({
3574
5083
  return `${mins}:${secs.toString().padStart(2, "0")}`;
3575
5084
  };
3576
5085
  const progress = duration > 0 ? currentTime / duration * 100 : 0;
3577
- return /* @__PURE__ */ React6.createElement(
5086
+ return /* @__PURE__ */ React21.createElement(
3578
5087
  "div",
3579
5088
  {
3580
5089
  className: `w-full max-w-4xl mx-auto px-6 py-4 bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl shadow-2xl pointer-events-auto transition-all group ${className}`
3581
5090
  },
3582
- /* @__PURE__ */ React6.createElement("div", { className: "relative w-full h-1.5 bg-white/20 rounded-full mb-4 cursor-pointer group/progress overflow-hidden" }, /* @__PURE__ */ React6.createElement(
5091
+ /* @__PURE__ */ React21.createElement("div", { className: "relative w-full h-1.5 bg-white/20 rounded-full mb-4 cursor-pointer group/progress overflow-hidden" }, /* @__PURE__ */ React21.createElement(
3583
5092
  "div",
3584
5093
  {
3585
5094
  className: "absolute h-full bg-blue-500 rounded-full transition-all duration-300",
3586
5095
  style: { width: `${progress}%` }
3587
5096
  }
3588
- ), /* @__PURE__ */ React6.createElement(
5097
+ ), /* @__PURE__ */ React21.createElement(
3589
5098
  "input",
3590
5099
  {
3591
5100
  type: "range",
@@ -3596,52 +5105,52 @@ var MusicControls = ({
3596
5105
  className: "absolute inset-0 w-full h-full opacity-0 cursor-pointer"
3597
5106
  }
3598
5107
  )),
3599
- /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center gap-2 text-xs font-mono text-white/60 w-32" }, /* @__PURE__ */ React6.createElement("span", null, formatTime(currentTime)), /* @__PURE__ */ React6.createElement("span", null, "/"), /* @__PURE__ */ React6.createElement("span", null, formatTime(duration))), /* @__PURE__ */ React6.createElement("div", { className: "flex items-center gap-6" }, /* @__PURE__ */ React6.createElement(
5108
+ /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center gap-2 text-xs font-mono text-white/60 w-32" }, /* @__PURE__ */ React21.createElement("span", null, formatTime(currentTime)), /* @__PURE__ */ React21.createElement("span", null, "/"), /* @__PURE__ */ React21.createElement("span", null, formatTime(duration))), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center gap-6" }, /* @__PURE__ */ React21.createElement(
3600
5109
  "button",
3601
5110
  {
3602
5111
  onClick: onPrevious,
3603
5112
  className: "text-white/80 hover:text-white transition-colors"
3604
5113
  },
3605
- /* @__PURE__ */ React6.createElement(SkipBack, { className: "w-6 h-6 fill-current" })
3606
- ), /* @__PURE__ */ React6.createElement(
5114
+ /* @__PURE__ */ React21.createElement(SkipBack, { className: "w-6 h-6 fill-current" })
5115
+ ), /* @__PURE__ */ React21.createElement(
3607
5116
  "button",
3608
5117
  {
3609
5118
  onClick: onPlayPause,
3610
5119
  className: "w-12 h-12 flex items-center justify-center bg-blue-500 hover:bg-blue-400 text-white rounded-full shadow-lg shadow-blue-500/20 transition-all hover:scale-105 active:scale-95"
3611
5120
  },
3612
- isPlaying ? /* @__PURE__ */ React6.createElement(Pause, { className: "w-6 h-6 fill-current" }) : /* @__PURE__ */ React6.createElement(Play, { className: "w-6 h-6 fill-current ml-1" })
3613
- ), /* @__PURE__ */ React6.createElement(
5121
+ isPlaying ? /* @__PURE__ */ React21.createElement(Pause, { className: "w-6 h-6 fill-current" }) : /* @__PURE__ */ React21.createElement(Play, { className: "w-6 h-6 fill-current ml-1" })
5122
+ ), /* @__PURE__ */ React21.createElement(
3614
5123
  "button",
3615
5124
  {
3616
5125
  onClick: onNext,
3617
5126
  className: "text-white/80 hover:text-white transition-colors"
3618
5127
  },
3619
- /* @__PURE__ */ React6.createElement(SkipForward, { className: "w-6 h-6 fill-current" })
3620
- )), /* @__PURE__ */ React6.createElement("div", { className: "flex items-center gap-4 w-32 justify-end" }, isCameraManual && /* @__PURE__ */ React6.createElement(
5128
+ /* @__PURE__ */ React21.createElement(SkipForward, { className: "w-6 h-6 fill-current" })
5129
+ )), /* @__PURE__ */ React21.createElement("div", { className: "flex items-center gap-4 w-32 justify-end" }, isCameraManual && /* @__PURE__ */ React21.createElement(
3621
5130
  "button",
3622
5131
  {
3623
5132
  onClick: onResetCamera,
3624
5133
  className: "text-blue-400 hover:text-blue-300 transition-colors animate-in zoom-in duration-300",
3625
5134
  title: "\u6062\u590D\u521D\u59CB\u89C6\u89D2"
3626
5135
  },
3627
- /* @__PURE__ */ React6.createElement(Camera, { className: "w-5 h-5" })
3628
- ), /* @__PURE__ */ React6.createElement(
5136
+ /* @__PURE__ */ React21.createElement(Camera, { className: "w-5 h-5" })
5137
+ ), /* @__PURE__ */ React21.createElement(
3629
5138
  "button",
3630
5139
  {
3631
5140
  onClick: onToggleLoop,
3632
5141
  className: "text-white/60 hover:text-white transition-colors",
3633
5142
  title: loopMode
3634
5143
  },
3635
- loopMode === "list" && /* @__PURE__ */ React6.createElement(Repeat, { className: "w-5 h-5" }),
3636
- loopMode === "single" && /* @__PURE__ */ React6.createElement(Repeat1, { className: "w-5 h-5 text-blue-400" }),
3637
- loopMode === "shuffle" && /* @__PURE__ */ React6.createElement(Shuffle, { className: "w-5 h-5 text-orange-400" })
3638
- ), /* @__PURE__ */ React6.createElement(
5144
+ loopMode === "list" && /* @__PURE__ */ React21.createElement(Repeat, { className: "w-5 h-5" }),
5145
+ loopMode === "single" && /* @__PURE__ */ React21.createElement(Repeat1, { className: "w-5 h-5 text-blue-400" }),
5146
+ loopMode === "shuffle" && /* @__PURE__ */ React21.createElement(Shuffle, { className: "w-5 h-5 text-orange-400" })
5147
+ ), /* @__PURE__ */ React21.createElement(
3639
5148
  "button",
3640
5149
  {
3641
5150
  onClick: onTogglePlaylist,
3642
5151
  className: "text-white/60 hover:text-white transition-colors"
3643
5152
  },
3644
- /* @__PURE__ */ React6.createElement(ListMusic, { className: "w-5 h-5" })
5153
+ /* @__PURE__ */ React21.createElement(ListMusic, { className: "w-5 h-5" })
3645
5154
  )))
3646
5155
  );
3647
5156
  };
@@ -3656,25 +5165,25 @@ var PlaylistPanel = ({
3656
5165
  onSearch,
3657
5166
  isSearching
3658
5167
  }) => {
3659
- const [searchValue, setSearchValue] = React6.useState("");
5168
+ const [searchValue, setSearchValue] = React21.useState("");
3660
5169
  if (!isOpen) return null;
3661
5170
  const handleSearchSubmit = (e) => {
3662
5171
  e.preventDefault();
3663
5172
  onSearch?.(searchValue);
3664
5173
  };
3665
- return /* @__PURE__ */ React6.createElement(
5174
+ return /* @__PURE__ */ React21.createElement(
3666
5175
  "div",
3667
5176
  {
3668
5177
  className: `fixed inset-y-0 right-0 w-80 bg-gray-900/90 backdrop-blur-2xl border-l border-white/10 shadow-2xl z-50 flex flex-col pointer-events-auto transform transition-transform duration-500 ease-out ${isOpen ? "translate-x-0" : "translate-x-full"} ${className}`
3669
5178
  },
3670
- /* @__PURE__ */ React6.createElement("div", { className: "flex flex-col p-6 border-b border-white/10 gap-4" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React6.createElement(Music, { className: "w-5 h-5 text-blue-400" }), /* @__PURE__ */ React6.createElement("h3", { className: "text-lg font-bold text-white" }, mikuMode ? "Miku \u6B4C\u66F2\u5E93" : "\u64AD\u653E\u5217\u8868")), /* @__PURE__ */ React6.createElement(
5179
+ /* @__PURE__ */ React21.createElement("div", { className: "flex flex-col p-6 border-b border-white/10 gap-4" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React21.createElement(Music, { className: "w-5 h-5 text-blue-400" }), /* @__PURE__ */ React21.createElement("h3", { className: "text-lg font-bold text-white" }, mikuMode ? "Miku \u6B4C\u66F2\u5E93" : "\u64AD\u653E\u5217\u8868")), /* @__PURE__ */ React21.createElement(
3671
5180
  "button",
3672
5181
  {
3673
5182
  onClick: onClose,
3674
5183
  className: "p-2 text-white/60 hover:text-white transition-colors"
3675
5184
  },
3676
- /* @__PURE__ */ React6.createElement(X, { className: "w-5 h-5" })
3677
- )), mikuMode && /* @__PURE__ */ React6.createElement("form", { onSubmit: handleSearchSubmit, className: "relative" }, /* @__PURE__ */ React6.createElement(
5185
+ /* @__PURE__ */ React21.createElement(X, { className: "w-5 h-5" })
5186
+ )), mikuMode && /* @__PURE__ */ React21.createElement("form", { onSubmit: handleSearchSubmit, className: "relative" }, /* @__PURE__ */ React21.createElement(
3678
5187
  "input",
3679
5188
  {
3680
5189
  type: "text",
@@ -3683,23 +5192,23 @@ var PlaylistPanel = ({
3683
5192
  placeholder: "\u641C\u7D22 Miku \u6B4C\u66F2...",
3684
5193
  className: "w-full bg-white/5 border border-white/10 rounded-lg py-2 pl-10 pr-4 text-sm text-white placeholder:text-white/20 focus:outline-none focus:border-blue-500/50 transition-colors"
3685
5194
  }
3686
- ), /* @__PURE__ */ React6.createElement(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-white/20" }), isSearching && /* @__PURE__ */ React6.createElement(Loader2, { className: "absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-blue-400 animate-spin" }))),
3687
- /* @__PURE__ */ React6.createElement("div", { className: "flex-1 overflow-y-auto p-4 space-y-2 custom-scrollbar" }, tracks.map((track, index) => {
5195
+ ), /* @__PURE__ */ React21.createElement(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-white/20" }), isSearching && /* @__PURE__ */ React21.createElement(Loader2, { className: "absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-blue-400 animate-spin" }))),
5196
+ /* @__PURE__ */ React21.createElement("div", { className: "flex-1 overflow-y-auto p-4 space-y-2 custom-scrollbar" }, tracks.map((track, index) => {
3688
5197
  const isActive = index === currentIndex;
3689
- return /* @__PURE__ */ React6.createElement(
5198
+ return /* @__PURE__ */ React21.createElement(
3690
5199
  "button",
3691
5200
  {
3692
5201
  key: track.id,
3693
5202
  onClick: () => onSelectTrack(index),
3694
5203
  className: `w-full flex items-center gap-4 p-3 rounded-xl transition-all group ${isActive ? "bg-blue-500/20 border border-blue-500/30" : "hover:bg-white/5 border border-transparent"}`
3695
5204
  },
3696
- /* @__PURE__ */ React6.createElement("div", { className: "relative w-12 h-12 flex-shrink-0 rounded-lg overflow-hidden bg-gray-800" }, track.coverUrl ? /* @__PURE__ */ React6.createElement("img", { src: track.coverUrl, alt: track.title, className: "w-full h-full object-cover" }) : /* @__PURE__ */ React6.createElement("div", { className: "w-full h-full flex items-center justify-center text-white/20" }, /* @__PURE__ */ React6.createElement(Music, { className: "w-6 h-6" })), isActive && /* @__PURE__ */ React6.createElement("div", { className: "absolute inset-0 bg-blue-500/40 flex items-center justify-center" }, /* @__PURE__ */ React6.createElement("div", { className: "flex gap-1 items-end h-4" }, /* @__PURE__ */ React6.createElement("div", { className: "w-1 bg-white animate-music-bar-1" }), /* @__PURE__ */ React6.createElement("div", { className: "w-1 bg-white animate-music-bar-2" }), /* @__PURE__ */ React6.createElement("div", { className: "w-1 bg-white animate-music-bar-3" })))),
3697
- /* @__PURE__ */ React6.createElement("div", { className: "flex-1 text-left min-w-0" }, /* @__PURE__ */ React6.createElement("h4", { className: `text-sm font-bold truncate ${isActive ? "text-blue-400" : "text-white/90"}` }, track.title), /* @__PURE__ */ React6.createElement("p", { className: "text-xs text-white/40 truncate mt-0.5" }, track.artist || "\u672A\u77E5\u827A\u672F\u5BB6")),
3698
- !isActive && /* @__PURE__ */ React6.createElement("div", { className: "opacity-0 group-hover:opacity-100 transition-opacity" }, /* @__PURE__ */ React6.createElement(Play, { className: "w-4 h-4 text-white/40 fill-current" }))
5205
+ /* @__PURE__ */ React21.createElement("div", { className: "relative w-12 h-12 flex-shrink-0 rounded-lg overflow-hidden bg-gray-800" }, track.coverUrl ? /* @__PURE__ */ React21.createElement("img", { src: track.coverUrl, alt: track.title, className: "w-full h-full object-cover" }) : /* @__PURE__ */ React21.createElement("div", { className: "w-full h-full flex items-center justify-center text-white/20" }, /* @__PURE__ */ React21.createElement(Music, { className: "w-6 h-6" })), isActive && /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 bg-blue-500/40 flex items-center justify-center" }, /* @__PURE__ */ React21.createElement("div", { className: "flex gap-1 items-end h-4" }, /* @__PURE__ */ React21.createElement("div", { className: "w-1 bg-white animate-music-bar-1" }), /* @__PURE__ */ React21.createElement("div", { className: "w-1 bg-white animate-music-bar-2" }), /* @__PURE__ */ React21.createElement("div", { className: "w-1 bg-white animate-music-bar-3" })))),
5206
+ /* @__PURE__ */ React21.createElement("div", { className: "flex-1 text-left min-w-0" }, /* @__PURE__ */ React21.createElement("h4", { className: `text-sm font-bold truncate ${isActive ? "text-blue-400" : "text-white/90"}` }, track.title), /* @__PURE__ */ React21.createElement("p", { className: "text-xs text-white/40 truncate mt-0.5" }, track.artist || "\u672A\u77E5\u827A\u672F\u5BB6")),
5207
+ !isActive && /* @__PURE__ */ React21.createElement("div", { className: "opacity-0 group-hover:opacity-100 transition-opacity" }, /* @__PURE__ */ React21.createElement(Play, { className: "w-4 h-4 text-white/40 fill-current" }))
3699
5208
  );
3700
5209
  })),
3701
- /* @__PURE__ */ React6.createElement("div", { className: "p-6 border-t border-white/10" }, /* @__PURE__ */ React6.createElement("p", { className: "text-xs text-gray-500 text-center" }, "\u5171 ", tracks.length, " \u9996\u66F2\u76EE")),
3702
- /* @__PURE__ */ React6.createElement("style", { dangerouslySetInnerHTML: { __html: `
5210
+ /* @__PURE__ */ React21.createElement("div", { className: "p-6 border-t border-white/10" }, /* @__PURE__ */ React21.createElement("p", { className: "text-xs text-gray-500 text-center" }, "\u5171 ", tracks.length, " \u9996\u66F2\u76EE")),
5211
+ /* @__PURE__ */ React21.createElement("style", { dangerouslySetInnerHTML: { __html: `
3703
5212
  .custom-scrollbar::-webkit-scrollbar {
3704
5213
  width: 4px;
3705
5214
  }
@@ -3732,7 +5241,7 @@ var PlaylistPanel = ({
3732
5241
  );
3733
5242
  };
3734
5243
  var TrackInfo = ({ track, className = "" }) => {
3735
- return /* @__PURE__ */ React6.createElement("div", { className: `flex flex-col items-center text-center gap-2 ${className}` }, /* @__PURE__ */ React6.createElement("div", { className: "px-4 py-1.5 bg-black/40 backdrop-blur-md rounded-full border border-white/10 shadow-lg" }, /* @__PURE__ */ React6.createElement("h2", { className: "text-lg font-bold text-white tracking-wider truncate max-w-md" }, track.title)), /* @__PURE__ */ React6.createElement("p", { className: "text-sm font-medium text-white/60 drop-shadow-md" }, track.artist || "\u672A\u77E5\u827A\u672F\u5BB6"));
5244
+ return /* @__PURE__ */ React21.createElement("div", { className: `flex flex-col items-center text-center gap-2 ${className}` }, /* @__PURE__ */ React21.createElement("div", { className: "px-4 py-1.5 bg-black/40 backdrop-blur-md rounded-full border border-white/10 shadow-lg" }, /* @__PURE__ */ React21.createElement("h2", { className: "text-lg font-bold text-white tracking-wider truncate max-w-md" }, track.title)), /* @__PURE__ */ React21.createElement("p", { className: "text-sm font-medium text-white/60 drop-shadow-md" }, track.artist || "\u672A\u77E5\u827A\u672F\u5BB6"));
3736
5245
  };
3737
5246
 
3738
5247
  // src/mmd/music-player/MMDMusicPlayer.tsx
@@ -3934,9 +5443,9 @@ var MMDMusicPlayer = forwardRef(
3934
5443
  };
3935
5444
  }, [resetUITimeout]);
3936
5445
  if (!currentTrack) {
3937
- return /* @__PURE__ */ React6.createElement("div", { className: "flex h-full w-full items-center justify-center bg-black text-white" }, "\u64AD\u653E\u5217\u8868\u4E3A\u7A7A");
5446
+ return /* @__PURE__ */ React21.createElement("div", { className: "flex h-full w-full items-center justify-center bg-black text-white" }, "\u64AD\u653E\u5217\u8868\u4E3A\u7A7A");
3938
5447
  }
3939
- return /* @__PURE__ */ React6.createElement(
5448
+ return /* @__PURE__ */ React21.createElement(
3940
5449
  "div",
3941
5450
  {
3942
5451
  ref: containerRef,
@@ -3945,7 +5454,7 @@ var MMDMusicPlayer = forwardRef(
3945
5454
  onMouseMove: resetUITimeout,
3946
5455
  onClick: resetUITimeout
3947
5456
  },
3948
- /* @__PURE__ */ React6.createElement(
5457
+ /* @__PURE__ */ React21.createElement(
3949
5458
  "div",
3950
5459
  {
3951
5460
  className: "absolute inset-0 w-full h-full",
@@ -3955,7 +5464,7 @@ var MMDMusicPlayer = forwardRef(
3955
5464
  transition: "opacity 0.5s ease-in-out"
3956
5465
  }
3957
5466
  },
3958
- !isTransitioning && /* @__PURE__ */ React6.createElement(
5467
+ !isTransitioning && /* @__PURE__ */ React21.createElement(
3959
5468
  MMDPlayerBase,
3960
5469
  {
3961
5470
  ref: playerRef,
@@ -3989,14 +5498,14 @@ var MMDMusicPlayer = forwardRef(
3989
5498
  }
3990
5499
  )
3991
5500
  ),
3992
- (isLoading || isTransitioning) && /* @__PURE__ */ React6.createElement("div", { className: "absolute inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md" }, /* @__PURE__ */ React6.createElement("div", { className: "flex flex-col items-center gap-4" }, /* @__PURE__ */ React6.createElement("div", { className: "h-12 w-12 animate-spin rounded-full border-4 border-white/20 border-t-blue-500" }), /* @__PURE__ */ React6.createElement("div", { className: "text-white font-medium" }, isTransitioning ? "\u51C6\u5907\u4E0B\u4E00\u9996..." : "\u6B63\u5728\u52A0\u8F7D\u821E\u53F0..."))),
3993
- /* @__PURE__ */ React6.createElement(
5501
+ (isLoading || isTransitioning) && /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md" }, /* @__PURE__ */ React21.createElement("div", { className: "flex flex-col items-center gap-4" }, /* @__PURE__ */ React21.createElement("div", { className: "h-12 w-12 animate-spin rounded-full border-4 border-white/20 border-t-blue-500" }), /* @__PURE__ */ React21.createElement("div", { className: "text-white font-medium" }, isTransitioning ? "\u51C6\u5907\u4E0B\u4E00\u9996..." : "\u6B63\u5728\u52A0\u8F7D\u821E\u53F0..."))),
5502
+ /* @__PURE__ */ React21.createElement(
3994
5503
  "div",
3995
5504
  {
3996
5505
  className: `absolute inset-0 z-10 flex flex-col justify-between transition-opacity duration-700 pointer-events-none ${isUIVisible ? "opacity-100" : "opacity-0"}`
3997
5506
  },
3998
- /* @__PURE__ */ React6.createElement("div", { className: "pt-12 px-8 flex justify-center" }, /* @__PURE__ */ React6.createElement(TrackInfo, { track: currentTrack })),
3999
- /* @__PURE__ */ React6.createElement("div", { className: "pb-12 px-8" }, /* @__PURE__ */ React6.createElement(
5507
+ /* @__PURE__ */ React21.createElement("div", { className: "pt-12 px-8 flex justify-center" }, /* @__PURE__ */ React21.createElement(TrackInfo, { track: currentTrack })),
5508
+ /* @__PURE__ */ React21.createElement("div", { className: "pb-12 px-8" }, /* @__PURE__ */ React21.createElement(
4000
5509
  MusicControls,
4001
5510
  {
4002
5511
  isPlaying,
@@ -4021,7 +5530,7 @@ var MMDMusicPlayer = forwardRef(
4021
5530
  }
4022
5531
  ))
4023
5532
  ),
4024
- /* @__PURE__ */ React6.createElement(
5533
+ /* @__PURE__ */ React21.createElement(
4025
5534
  PlaylistPanel,
4026
5535
  {
4027
5536
  tracks,
@@ -4159,13 +5668,13 @@ var MMDARPlayer = forwardRef((props, ref) => {
4159
5668
  }
4160
5669
  return () => stopCamera();
4161
5670
  }, [autoPlay, startCamera, stopCamera]);
4162
- return /* @__PURE__ */ React6.createElement(
5671
+ return /* @__PURE__ */ React21.createElement(
4163
5672
  "div",
4164
5673
  {
4165
5674
  className: `relative w-full h-full bg-black overflow-hidden ${className}`,
4166
5675
  style
4167
5676
  },
4168
- /* @__PURE__ */ React6.createElement(
5677
+ /* @__PURE__ */ React21.createElement(
4169
5678
  "video",
4170
5679
  {
4171
5680
  ref: videoRef,
@@ -4176,13 +5685,13 @@ var MMDARPlayer = forwardRef((props, ref) => {
4176
5685
  style: { zIndex: 0 }
4177
5686
  }
4178
5687
  ),
4179
- /* @__PURE__ */ React6.createElement(
5688
+ /* @__PURE__ */ React21.createElement(
4180
5689
  "div",
4181
5690
  {
4182
5691
  className: "absolute inset-0 w-full h-full",
4183
5692
  style: { zIndex: 1 }
4184
5693
  },
4185
- /* @__PURE__ */ React6.createElement(
5694
+ /* @__PURE__ */ React21.createElement(
4186
5695
  MMDPlayerBase,
4187
5696
  {
4188
5697
  ref: playerRef,
@@ -4205,45 +5714,45 @@ var MMDARPlayer = forwardRef((props, ref) => {
4205
5714
  }
4206
5715
  )
4207
5716
  ),
4208
- /* @__PURE__ */ React6.createElement("div", { className: "absolute inset-0 z-10 pointer-events-none flex flex-col justify-between p-6" }, /* @__PURE__ */ React6.createElement("div", { className: "flex justify-between items-start pointer-events-auto" }, cameraError ? /* @__PURE__ */ React6.createElement("div", { className: "bg-red-500/80 backdrop-blur-md text-white px-4 py-2 rounded-full flex items-center gap-2 text-sm" }, /* @__PURE__ */ React6.createElement(AlertCircle, { className: "w-4 h-4" }), cameraError, /* @__PURE__ */ React6.createElement(
5717
+ /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 z-10 pointer-events-none flex flex-col justify-between p-6" }, /* @__PURE__ */ React21.createElement("div", { className: "flex justify-between items-start pointer-events-auto" }, cameraError ? /* @__PURE__ */ React21.createElement("div", { className: "bg-red-500/80 backdrop-blur-md text-white px-4 py-2 rounded-full flex items-center gap-2 text-sm" }, /* @__PURE__ */ React21.createElement(AlertCircle, { className: "w-4 h-4" }), cameraError, /* @__PURE__ */ React21.createElement(
4209
5718
  "button",
4210
5719
  {
4211
5720
  onClick: () => startCamera(),
4212
5721
  className: "ml-2 underline"
4213
5722
  },
4214
5723
  "\u91CD\u8BD5"
4215
- )) : /* @__PURE__ */ React6.createElement("div", { className: "bg-black/40 backdrop-blur-md text-white px-4 py-2 rounded-full flex items-center gap-2 text-sm" }, /* @__PURE__ */ React6.createElement(Camera, { className: "w-4 h-4 text-green-400" }), isCameraStarted ? "\u5B9E\u666F AR \u6A21\u5F0F\u5DF2\u5F00\u542F" : "\u7B49\u5F85\u6444\u50CF\u5934..."), /* @__PURE__ */ React6.createElement("div", { className: "flex flex-col gap-2" }, showSettings && /* @__PURE__ */ React6.createElement(
5724
+ )) : /* @__PURE__ */ React21.createElement("div", { className: "bg-black/40 backdrop-blur-md text-white px-4 py-2 rounded-full flex items-center gap-2 text-sm" }, /* @__PURE__ */ React21.createElement(Camera, { className: "w-4 h-4 text-green-400" }), isCameraStarted ? "\u5B9E\u666F AR \u6A21\u5F0F\u5DF2\u5F00\u542F" : "\u7B49\u5F85\u6444\u50CF\u5934..."), /* @__PURE__ */ React21.createElement("div", { className: "flex flex-col gap-2" }, showSettings && /* @__PURE__ */ React21.createElement(
4216
5725
  "button",
4217
5726
  {
4218
5727
  onClick: () => setIsSettingsOpen(!isSettingsOpen),
4219
5728
  className: `p-3 backdrop-blur-md rounded-full text-white transition-all active:scale-95 pointer-events-auto ${isSettingsOpen ? "bg-blue-500" : "bg-white/10 hover:bg-white/20"}`,
4220
5729
  title: "\u8BBE\u7F6E\u6A21\u578B\u4E0E\u52A8\u4F5C"
4221
5730
  },
4222
- /* @__PURE__ */ React6.createElement(Settings, { className: "w-5 h-5" })
4223
- ), /* @__PURE__ */ React6.createElement(
5731
+ /* @__PURE__ */ React21.createElement(Settings, { className: "w-5 h-5" })
5732
+ ), /* @__PURE__ */ React21.createElement(
4224
5733
  "button",
4225
5734
  {
4226
5735
  onClick: switchCamera,
4227
5736
  className: "p-3 bg-white/10 hover:bg-white/20 backdrop-blur-md rounded-full text-white transition-all active:scale-95 pointer-events-auto",
4228
5737
  title: "\u5207\u6362\u524D\u540E\u6444\u50CF\u5934"
4229
5738
  },
4230
- /* @__PURE__ */ React6.createElement(RefreshCw, { className: "w-5 h-5" })
4231
- ), /* @__PURE__ */ React6.createElement(
5739
+ /* @__PURE__ */ React21.createElement(RefreshCw, { className: "w-5 h-5" })
5740
+ ), /* @__PURE__ */ React21.createElement(
4232
5741
  "button",
4233
5742
  {
4234
5743
  onClick: isCameraStarted ? stopCamera : () => startCamera(),
4235
5744
  className: `p-3 backdrop-blur-md rounded-full text-white transition-all active:scale-95 pointer-events-auto ${isCameraStarted ? "bg-red-500/20 hover:bg-red-500/40" : "bg-green-500/20 hover:bg-green-500/40"}`,
4236
5745
  title: isCameraStarted ? "\u5173\u95ED\u6444\u50CF\u5934" : "\u5F00\u542F\u6444\u50CF\u5934"
4237
5746
  },
4238
- isCameraStarted ? /* @__PURE__ */ React6.createElement(CameraOff, { className: "w-5 h-5" }) : /* @__PURE__ */ React6.createElement(Camera, { className: "w-5 h-5" })
4239
- ))), isSettingsOpen && /* @__PURE__ */ React6.createElement("div", { className: "absolute top-20 right-6 w-80 bg-black/80 backdrop-blur-xl border border-white/10 rounded-2xl p-6 pointer-events-auto shadow-2xl animate-in slide-in-from-right-4 duration-300" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between mb-6" }, /* @__PURE__ */ React6.createElement("h3", { className: "text-white font-bold flex items-center gap-2" }, /* @__PURE__ */ React6.createElement(Settings, { className: "w-4 h-4" }), "\u8D44\u6E90\u8BBE\u7F6E"), /* @__PURE__ */ React6.createElement(
5747
+ isCameraStarted ? /* @__PURE__ */ React21.createElement(CameraOff, { className: "w-5 h-5" }) : /* @__PURE__ */ React21.createElement(Camera, { className: "w-5 h-5" })
5748
+ ))), isSettingsOpen && /* @__PURE__ */ React21.createElement("div", { className: "absolute top-20 right-6 w-80 bg-black/80 backdrop-blur-xl border border-white/10 rounded-2xl p-6 pointer-events-auto shadow-2xl animate-in slide-in-from-right-4 duration-300" }, /* @__PURE__ */ React21.createElement("div", { className: "flex items-center justify-between mb-6" }, /* @__PURE__ */ React21.createElement("h3", { className: "text-white font-bold flex items-center gap-2" }, /* @__PURE__ */ React21.createElement(Settings, { className: "w-4 h-4" }), "\u8D44\u6E90\u8BBE\u7F6E"), /* @__PURE__ */ React21.createElement(
4240
5749
  "button",
4241
5750
  {
4242
5751
  onClick: () => setIsSettingsOpen(false),
4243
5752
  className: "p-1 hover:bg-white/10 rounded-full transition-colors text-white/60"
4244
5753
  },
4245
- /* @__PURE__ */ React6.createElement(X, { className: "w-4 h-4" })
4246
- )), /* @__PURE__ */ React6.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React6.createElement("div", null, /* @__PURE__ */ React6.createElement("label", { className: "block text-xs font-medium text-white/50 mb-1.5 ml-1 uppercase tracking-wider" }, "\u6A21\u578B\u8DEF\u5F84 (.pmx)"), /* @__PURE__ */ React6.createElement(
5754
+ /* @__PURE__ */ React21.createElement(X, { className: "w-4 h-4" })
5755
+ )), /* @__PURE__ */ React21.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React21.createElement("div", null, /* @__PURE__ */ React21.createElement("label", { className: "block text-xs font-medium text-white/50 mb-1.5 ml-1 uppercase tracking-wider" }, "\u6A21\u578B\u8DEF\u5F84 (.pmx)"), /* @__PURE__ */ React21.createElement(
4247
5756
  "input",
4248
5757
  {
4249
5758
  type: "text",
@@ -4252,7 +5761,7 @@ var MMDARPlayer = forwardRef((props, ref) => {
4252
5761
  placeholder: "\u8BF7\u8F93\u5165\u6A21\u578B URL...",
4253
5762
  className: "w-full bg-white/5 border border-white/10 rounded-xl px-4 py-2.5 text-sm text-white placeholder:text-white/10 focus:outline-none focus:border-blue-500/50 transition-colors"
4254
5763
  }
4255
- )), /* @__PURE__ */ React6.createElement("div", null, /* @__PURE__ */ React6.createElement("label", { className: "block text-xs font-medium text-white/50 mb-1.5 ml-1 uppercase tracking-wider" }, "\u52A8\u4F5C\u8DEF\u5F84 (.vmd)"), /* @__PURE__ */ React6.createElement(
5764
+ )), /* @__PURE__ */ React21.createElement("div", null, /* @__PURE__ */ React21.createElement("label", { className: "block text-xs font-medium text-white/50 mb-1.5 ml-1 uppercase tracking-wider" }, "\u52A8\u4F5C\u8DEF\u5F84 (.vmd)"), /* @__PURE__ */ React21.createElement(
4256
5765
  "input",
4257
5766
  {
4258
5767
  type: "text",
@@ -4261,7 +5770,7 @@ var MMDARPlayer = forwardRef((props, ref) => {
4261
5770
  placeholder: "\u8BF7\u8F93\u5165\u52A8\u4F5C URL...",
4262
5771
  className: "w-full bg-white/5 border border-white/10 rounded-xl px-4 py-2.5 text-sm text-white placeholder:text-white/10 focus:outline-none focus:border-blue-500/50 transition-colors"
4263
5772
  }
4264
- )), /* @__PURE__ */ React6.createElement("div", { className: "pt-2" }, /* @__PURE__ */ React6.createElement(
5773
+ )), /* @__PURE__ */ React21.createElement("div", { className: "pt-2" }, /* @__PURE__ */ React21.createElement(
4265
5774
  "button",
4266
5775
  {
4267
5776
  onClick: () => {
@@ -4272,13 +5781,557 @@ var MMDARPlayer = forwardRef((props, ref) => {
4272
5781
  },
4273
5782
  className: "w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-3 rounded-xl flex items-center justify-center gap-2 transition-all active:scale-95 shadow-lg shadow-blue-500/20"
4274
5783
  },
4275
- /* @__PURE__ */ React6.createElement(Check, { className: "w-4 h-4" }),
5784
+ /* @__PURE__ */ React21.createElement(Check, { className: "w-4 h-4" }),
4276
5785
  "\u5E94\u7528\u66F4\u6539"
4277
- )))), isLoading && /* @__PURE__ */ React6.createElement("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none" }, /* @__PURE__ */ React6.createElement("div", { className: "flex flex-col items-center gap-3" }, /* @__PURE__ */ React6.createElement("div", { className: "w-12 h-12 border-4 border-blue-500/30 border-t-blue-500 rounded-full animate-spin" }), /* @__PURE__ */ React6.createElement("div", { className: "text-white text-sm font-medium bg-black/40 px-3 py-1 rounded-full backdrop-blur-sm" }, "\u6B63\u5728\u52A0\u8F7D Miku \u8D44\u6E90..."))))
5786
+ )))), isLoading && /* @__PURE__ */ React21.createElement("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none" }, /* @__PURE__ */ React21.createElement("div", { className: "flex flex-col items-center gap-3" }, /* @__PURE__ */ React21.createElement("div", { className: "w-12 h-12 border-4 border-blue-500/30 border-t-blue-500 rounded-full animate-spin" }), /* @__PURE__ */ React21.createElement("div", { className: "text-white text-sm font-medium bg-black/40 px-3 py-1 rounded-full backdrop-blur-sm" }, "\u6B63\u5728\u52A0\u8F7D Miku \u8D44\u6E90..."))))
4278
5787
  );
4279
5788
  });
4280
5789
  MMDARPlayer.displayName = "MMDARPlayer";
4281
5790
 
4282
- export { ChoiceMenu, DialogueBox, HistoryPanel, LoadingOverlay, LoadingScreen, MMDARPlayer, MMDMusicPlayer, MMDPlayerBase, MMDPlayerEnhanced, MMDPlayerEnhancedDebugInfo, MMDPlaylist, MMDPlaylistDebugInfo, MMDVisualNovel, MusicControls, PlaylistPanel, StartScreen, TrackInfo, loadAmmo };
5791
+ // src/mmd/fx/utils.ts
5792
+ function exportFXToJSON(effect) {
5793
+ return JSON.stringify(effect, null, 2);
5794
+ }
5795
+ function exportFXToMarkdown(effect) {
5796
+ const lines = [];
5797
+ lines.push(`# ${effect.fileName}`);
5798
+ lines.push("");
5799
+ if (effect.defines.length > 0) {
5800
+ lines.push("## \u5B8F\u5B9A\u4E49 (Defines)");
5801
+ lines.push("");
5802
+ lines.push("| \u540D\u79F0 | \u503C | \u72B6\u6001 | \u8BF4\u660E |");
5803
+ lines.push("|------|-----|------|------|");
5804
+ effect.defines.forEach((d) => {
5805
+ const status = d.isCommented ? "\u7981\u7528" : "\u542F\u7528";
5806
+ lines.push(`| ${d.name} | ${d.value || "-"} | ${status} | ${d.comment || "-"} |`);
5807
+ });
5808
+ lines.push("");
5809
+ }
5810
+ if (effect.textures.length > 0) {
5811
+ lines.push("## \u7EB9\u7406 (Textures)");
5812
+ lines.push("");
5813
+ lines.push("| \u540D\u79F0 | \u8DEF\u5F84 | \u5C3A\u5BF8 | \u7528\u9014 |");
5814
+ lines.push("|------|------|------|------|");
5815
+ effect.textures.forEach((t) => {
5816
+ const size = t.width && t.height ? `${t.width}x${t.height}` : "-";
5817
+ lines.push(`| ${t.name} | ${t.path} | ${size} | ${t.purpose || "-"} |`);
5818
+ });
5819
+ lines.push("");
5820
+ }
5821
+ if (effect.parameters.length > 0) {
5822
+ lines.push("## \u53C2\u6570 (Parameters)");
5823
+ lines.push("");
5824
+ lines.push("| \u540D\u79F0 | \u7C7B\u578B | \u8BED\u4E49 | \u9ED8\u8BA4\u503C |");
5825
+ lines.push("|------|------|------|--------|");
5826
+ effect.parameters.forEach((p) => {
5827
+ lines.push(`| ${p.name} | ${p.type} | ${p.semantic || "-"} | ${p.defaultValue || "-"} |`);
5828
+ });
5829
+ lines.push("");
5830
+ }
5831
+ if (effect.controllers.length > 0) {
5832
+ lines.push("## \u63A7\u5236\u5668 (Controllers)");
5833
+ lines.push("");
5834
+ lines.push("| \u53C2\u6570 | \u5BF9\u8C61\u540D | \u63A7\u5236\u9879 |");
5835
+ lines.push("|------|--------|--------|");
5836
+ effect.controllers.forEach((c) => {
5837
+ lines.push(`| ${c.name} | ${c.objectName} | ${c.itemName} |`);
5838
+ });
5839
+ lines.push("");
5840
+ }
5841
+ if (effect.includes.length > 0) {
5842
+ lines.push("## \u5305\u542B\u6587\u4EF6 (Includes)");
5843
+ lines.push("");
5844
+ effect.includes.forEach((inc) => {
5845
+ lines.push(`- ${inc}`);
5846
+ });
5847
+ lines.push("");
5848
+ }
5849
+ if (effect.techniques.length > 0) {
5850
+ lines.push("## \u6280\u672F (Techniques)");
5851
+ lines.push("");
5852
+ effect.techniques.forEach((t) => {
5853
+ lines.push(`### ${t.name}`);
5854
+ lines.push("");
5855
+ t.passes.forEach((p, idx) => {
5856
+ lines.push(`#### Pass ${p.name || idx + 1}`);
5857
+ if (p.vertexShader) {
5858
+ lines.push(`- **\u9876\u70B9\u7740\u8272\u5668**: ${p.vertexShader.function} (${p.vertexShader.profile})`);
5859
+ }
5860
+ if (p.pixelShader) {
5861
+ lines.push(`- **\u50CF\u7D20\u7740\u8272\u5668**: ${p.pixelShader.function} (${p.pixelShader.profile})`);
5862
+ }
5863
+ lines.push("");
5864
+ });
5865
+ });
5866
+ }
5867
+ return lines.join("\n");
5868
+ }
5869
+ function compareFXEffects(effect1, effect2) {
5870
+ const diff = {
5871
+ addedDefines: [],
5872
+ removedDefines: [],
5873
+ changedDefines: [],
5874
+ addedTextures: [],
5875
+ removedTextures: [],
5876
+ addedParameters: [],
5877
+ removedParameters: []
5878
+ };
5879
+ const defines1Map = new Map(effect1.defines.map((d) => [d.name, d]));
5880
+ const defines2Map = new Map(effect2.defines.map((d) => [d.name, d]));
5881
+ defines2Map.forEach((define, name) => {
5882
+ if (!defines1Map.has(name)) {
5883
+ diff.addedDefines.push(name);
5884
+ } else {
5885
+ const oldDefine = defines1Map.get(name);
5886
+ if (oldDefine.value !== define.value || oldDefine.isCommented !== define.isCommented) {
5887
+ diff.changedDefines.push({
5888
+ name,
5889
+ oldValue: oldDefine.value,
5890
+ newValue: define.value
5891
+ });
5892
+ }
5893
+ }
5894
+ });
5895
+ defines1Map.forEach((define, name) => {
5896
+ if (!defines2Map.has(name)) {
5897
+ diff.removedDefines.push(name);
5898
+ }
5899
+ });
5900
+ const textures1Set = new Set(effect1.textures.map((t) => t.name));
5901
+ const textures2Set = new Set(effect2.textures.map((t) => t.name));
5902
+ textures2Set.forEach((name) => {
5903
+ if (!textures1Set.has(name)) {
5904
+ diff.addedTextures.push(name);
5905
+ }
5906
+ });
5907
+ textures1Set.forEach((name) => {
5908
+ if (!textures2Set.has(name)) {
5909
+ diff.removedTextures.push(name);
5910
+ }
5911
+ });
5912
+ const params1Set = new Set(effect1.parameters.map((p) => p.name));
5913
+ const params2Set = new Set(effect2.parameters.map((p) => p.name));
5914
+ params2Set.forEach((name) => {
5915
+ if (!params1Set.has(name)) {
5916
+ diff.addedParameters.push(name);
5917
+ }
5918
+ });
5919
+ params1Set.forEach((name) => {
5920
+ if (!params2Set.has(name)) {
5921
+ diff.removedParameters.push(name);
5922
+ }
5923
+ });
5924
+ return diff;
5925
+ }
5926
+ function filterDefinesByPrefix(defines, prefix) {
5927
+ return defines.filter((d) => d.name.startsWith(prefix));
5928
+ }
5929
+ function getTextureDefines(defines) {
5930
+ return filterDefinesByPrefix(defines, "BLEND");
5931
+ }
5932
+ function getFeatureFlags(defines) {
5933
+ return defines.filter(
5934
+ (d) => d.name.startsWith("USE_") || d.name.includes("FLAG") || d.name === "HANDLE_EDGE" || d.name === "MODEL_TOON"
5935
+ );
5936
+ }
5937
+ function getColorParameters(parameters) {
5938
+ return parameters.filter(
5939
+ (p) => p.name.toLowerCase().includes("color") || p.name.toLowerCase().includes("rgb") || p.name.toLowerCase().includes("hsv") || p.type === "float3" || p.type === "float4"
5940
+ );
5941
+ }
5942
+ function hasFeature(effect, featureName) {
5943
+ return effect.defines.some(
5944
+ (d) => d.name === featureName && !d.isCommented
5945
+ );
5946
+ }
5947
+ function getConfigSummaryText(effect) {
5948
+ const features = [];
5949
+ if (hasFeature(effect, "USE_LOCALSHADOW")) {
5950
+ features.push("LocalShadow");
5951
+ }
5952
+ if (hasFeature(effect, "USE_EXCELLENTSHADOW")) {
5953
+ features.push("ExcellentShadow");
5954
+ }
5955
+ if (hasFeature(effect, "USE_HGSHADOW")) {
5956
+ features.push("HgShadow");
5957
+ }
5958
+ if (hasFeature(effect, "HANDLE_EDGE")) {
5959
+ features.push("HandleEdge");
5960
+ }
5961
+ if (hasFeature(effect, "MODEL_TOON")) {
5962
+ features.push("ModelToon");
5963
+ }
5964
+ if (hasFeature(effect, "USE_ROUNDNORMAL")) {
5965
+ features.push("RoundNormal");
5966
+ }
5967
+ return features.length > 0 ? `\u542F\u7528\u529F\u80FD: ${features.join(", ")}` : "\u65E0\u7279\u6B8A\u529F\u80FD\u542F\u7528";
5968
+ }
5969
+ function extractTexturePaths(effect) {
5970
+ return effect.textures.map((t) => t.path);
5971
+ }
5972
+ function validateFXEffect(effect) {
5973
+ const errors = [];
5974
+ const warnings = [];
5975
+ if (effect.includes.length === 0) {
5976
+ warnings.push("\u6CA1\u6709\u627E\u5230include\u6307\u4EE4");
5977
+ }
5978
+ effect.textures.forEach((texture) => {
5979
+ if (!texture.path || texture.path === "") {
5980
+ errors.push(`\u7EB9\u7406 ${texture.name} \u7F3A\u5C11\u8DEF\u5F84`);
5981
+ }
5982
+ });
5983
+ effect.parameters.forEach((param) => {
5984
+ if (param.type.includes("float") && param.defaultValue) {
5985
+ const floatRegex = /^float[234]?\s*\(/;
5986
+ if (!floatRegex.test(param.defaultValue) && isNaN(parseFloat(param.defaultValue))) {
5987
+ warnings.push(`\u53C2\u6570 ${param.name} \u7684\u9ED8\u8BA4\u503C\u53EF\u80FD\u4E0D\u5408\u6CD5: ${param.defaultValue}`);
5988
+ }
5989
+ }
5990
+ });
5991
+ if (effect.techniques.length === 0 && effect.includes.length === 0) {
5992
+ warnings.push("\u6CA1\u6709\u627E\u5230Technique\u5B9A\u4E49\uFF0C\u4E5F\u6CA1\u6709include\u6587\u4EF6");
5993
+ }
5994
+ return {
5995
+ isValid: errors.length === 0,
5996
+ errors,
5997
+ warnings
5998
+ };
5999
+ }
6000
+ var FXViewer = ({
6001
+ source,
6002
+ isContent = false,
6003
+ fileName = "effect.fx",
6004
+ onParsed,
6005
+ onError,
6006
+ className = ""
6007
+ }) => {
6008
+ const [effect, setEffect] = useState(null);
6009
+ const [summary, setSummary] = useState(null);
6010
+ const [loading, setLoading] = useState(true);
6011
+ const [error, setError] = useState(null);
6012
+ const [activeTab, setActiveTab] = useState("summary");
6013
+ useEffect(() => {
6014
+ const parse = async () => {
6015
+ try {
6016
+ setLoading(true);
6017
+ setError(null);
6018
+ const parser = new FXParser();
6019
+ let parsedEffect;
6020
+ if (isContent) {
6021
+ parsedEffect = parser.parse(source, fileName);
6022
+ } else {
6023
+ parsedEffect = await parser.loadAndParse(source);
6024
+ }
6025
+ setEffect(parsedEffect);
6026
+ setSummary(parser.generateSummary(parsedEffect));
6027
+ onParsed?.(parsedEffect);
6028
+ } catch (err) {
6029
+ const errorMsg = err instanceof Error ? err.message : "\u89E3\u6790\u5931\u8D25";
6030
+ setError(errorMsg);
6031
+ onError?.(err instanceof Error ? err : new Error(errorMsg));
6032
+ } finally {
6033
+ setLoading(false);
6034
+ }
6035
+ };
6036
+ parse();
6037
+ }, [source, isContent, fileName, onParsed, onError]);
6038
+ if (loading) {
6039
+ return /* @__PURE__ */ React21.createElement("div", { className: `fx-viewer loading ${className}` }, /* @__PURE__ */ React21.createElement("div", { className: "fx-viewer-spinner" }, "\u52A0\u8F7D\u4E2D..."));
6040
+ }
6041
+ if (error || !effect || !summary) {
6042
+ return /* @__PURE__ */ React21.createElement("div", { className: `fx-viewer error ${className}` }, /* @__PURE__ */ React21.createElement("div", { className: "fx-viewer-error" }, /* @__PURE__ */ React21.createElement("h3", null, "\u274C \u89E3\u6790\u9519\u8BEF"), /* @__PURE__ */ React21.createElement("p", null, error || "\u672A\u77E5\u9519\u8BEF")));
6043
+ }
6044
+ const validation = validateFXEffect(effect);
6045
+ return /* @__PURE__ */ React21.createElement("div", { className: `fx-viewer ${className}` }, /* @__PURE__ */ React21.createElement("div", { className: "fx-viewer-header" }, /* @__PURE__ */ React21.createElement("h2", null, effect.fileName), /* @__PURE__ */ React21.createElement("p", { className: "fx-viewer-config-summary" }, getConfigSummaryText(effect))), /* @__PURE__ */ React21.createElement("div", { className: "fx-viewer-tabs" }, /* @__PURE__ */ React21.createElement(
6046
+ "button",
6047
+ {
6048
+ className: activeTab === "summary" ? "active" : "",
6049
+ onClick: () => setActiveTab("summary")
6050
+ },
6051
+ "\u6458\u8981"
6052
+ ), /* @__PURE__ */ React21.createElement(
6053
+ "button",
6054
+ {
6055
+ className: activeTab === "defines" ? "active" : "",
6056
+ onClick: () => setActiveTab("defines")
6057
+ },
6058
+ "\u5B8F\u5B9A\u4E49 (",
6059
+ summary.defineCount,
6060
+ ")"
6061
+ ), /* @__PURE__ */ React21.createElement(
6062
+ "button",
6063
+ {
6064
+ className: activeTab === "textures" ? "active" : "",
6065
+ onClick: () => setActiveTab("textures")
6066
+ },
6067
+ "\u7EB9\u7406 (",
6068
+ summary.textureCount,
6069
+ ")"
6070
+ ), /* @__PURE__ */ React21.createElement(
6071
+ "button",
6072
+ {
6073
+ className: activeTab === "parameters" ? "active" : "",
6074
+ onClick: () => setActiveTab("parameters")
6075
+ },
6076
+ "\u53C2\u6570 (",
6077
+ summary.parameterCount,
6078
+ ")"
6079
+ ), /* @__PURE__ */ React21.createElement(
6080
+ "button",
6081
+ {
6082
+ className: activeTab === "validation" ? "active" : "",
6083
+ onClick: () => setActiveTab("validation")
6084
+ },
6085
+ "\u9A8C\u8BC1"
6086
+ )), /* @__PURE__ */ React21.createElement("div", { className: "fx-viewer-content" }, activeTab === "summary" && /* @__PURE__ */ React21.createElement(SummaryTab, { summary, effect }), activeTab === "defines" && /* @__PURE__ */ React21.createElement(DefinesTab, { effect }), activeTab === "textures" && /* @__PURE__ */ React21.createElement(TexturesTab, { effect }), activeTab === "parameters" && /* @__PURE__ */ React21.createElement(ParametersTab, { effect }), activeTab === "validation" && /* @__PURE__ */ React21.createElement(ValidationTab, { validation })));
6087
+ };
6088
+ var SummaryTab = ({ summary, effect }) => /* @__PURE__ */ React21.createElement("div", { className: "fx-tab-summary" }, /* @__PURE__ */ React21.createElement("div", { className: "fx-stats-grid" }, /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-card" }, /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-label" }, "\u5B8F\u5B9A\u4E49"), /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-value" }, summary.defineCount)), /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-card" }, /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-label" }, "\u53C2\u6570"), /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-value" }, summary.parameterCount)), /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-card" }, /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-label" }, "\u7EB9\u7406"), /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-value" }, summary.textureCount)), /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-card" }, /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-label" }, "Technique"), /* @__PURE__ */ React21.createElement("div", { className: "fx-stat-value" }, summary.techniqueCount))), /* @__PURE__ */ React21.createElement("div", { className: "fx-features" }, /* @__PURE__ */ React21.createElement("h3", null, "\u529F\u80FD\u7279\u6027"), /* @__PURE__ */ React21.createElement("div", { className: "fx-feature-list" }, /* @__PURE__ */ React21.createElement("div", { className: `fx-feature ${summary.hasLocalShadow ? "enabled" : "disabled"}` }, summary.hasLocalShadow ? "\u2713" : "\u2717", " LocalShadow"), /* @__PURE__ */ React21.createElement("div", { className: `fx-feature ${summary.hasExcellentShadow ? "enabled" : "disabled"}` }, summary.hasExcellentShadow ? "\u2713" : "\u2717", " ExcellentShadow"), /* @__PURE__ */ React21.createElement("div", { className: `fx-feature ${summary.hasHgShadow ? "enabled" : "disabled"}` }, summary.hasHgShadow ? "\u2713" : "\u2717", " HgShadow"))), effect.includes.length > 0 && /* @__PURE__ */ React21.createElement("div", { className: "fx-includes" }, /* @__PURE__ */ React21.createElement("h3", null, "\u5305\u542B\u6587\u4EF6"), /* @__PURE__ */ React21.createElement("ul", null, effect.includes.map((inc, idx) => /* @__PURE__ */ React21.createElement("li", { key: idx }, /* @__PURE__ */ React21.createElement("code", null, inc))))), effect.controllers.length > 0 && /* @__PURE__ */ React21.createElement("div", { className: "fx-controllers" }, /* @__PURE__ */ React21.createElement("h3", null, "\u63A7\u5236\u5668"), /* @__PURE__ */ React21.createElement("ul", null, effect.controllers.map((ctrl, idx) => /* @__PURE__ */ React21.createElement("li", { key: idx }, /* @__PURE__ */ React21.createElement("strong", null, ctrl.name), ": ", ctrl.objectName, " / ", ctrl.itemName)))));
6089
+ var DefinesTab = ({ effect }) => /* @__PURE__ */ React21.createElement("div", { className: "fx-tab-defines" }, /* @__PURE__ */ React21.createElement("table", { className: "fx-table" }, /* @__PURE__ */ React21.createElement("thead", null, /* @__PURE__ */ React21.createElement("tr", null, /* @__PURE__ */ React21.createElement("th", null, "\u540D\u79F0"), /* @__PURE__ */ React21.createElement("th", null, "\u503C"), /* @__PURE__ */ React21.createElement("th", null, "\u72B6\u6001"), /* @__PURE__ */ React21.createElement("th", null, "\u884C\u53F7"))), /* @__PURE__ */ React21.createElement("tbody", null, effect.defines.map((define, idx) => /* @__PURE__ */ React21.createElement("tr", { key: idx, className: define.isCommented ? "disabled" : "enabled" }, /* @__PURE__ */ React21.createElement("td", null, /* @__PURE__ */ React21.createElement("code", null, define.name)), /* @__PURE__ */ React21.createElement("td", null, define.value || "-"), /* @__PURE__ */ React21.createElement("td", null, /* @__PURE__ */ React21.createElement("span", { className: `status-badge ${define.isCommented ? "disabled" : "enabled"}` }, define.isCommented ? "\u7981\u7528" : "\u542F\u7528")), /* @__PURE__ */ React21.createElement("td", null, define.lineNumber))))));
6090
+ var TexturesTab = ({ effect }) => /* @__PURE__ */ React21.createElement("div", { className: "fx-tab-textures" }, effect.textures.length === 0 ? /* @__PURE__ */ React21.createElement("p", { className: "fx-empty-message" }, "\u672A\u627E\u5230\u7EB9\u7406\u5B9A\u4E49") : /* @__PURE__ */ React21.createElement("table", { className: "fx-table" }, /* @__PURE__ */ React21.createElement("thead", null, /* @__PURE__ */ React21.createElement("tr", null, /* @__PURE__ */ React21.createElement("th", null, "\u540D\u79F0"), /* @__PURE__ */ React21.createElement("th", null, "\u8DEF\u5F84"), /* @__PURE__ */ React21.createElement("th", null, "\u5C3A\u5BF8"), /* @__PURE__ */ React21.createElement("th", null, "\u7528\u9014"))), /* @__PURE__ */ React21.createElement("tbody", null, effect.textures.map((texture, idx) => /* @__PURE__ */ React21.createElement("tr", { key: idx }, /* @__PURE__ */ React21.createElement("td", null, /* @__PURE__ */ React21.createElement("code", null, texture.name)), /* @__PURE__ */ React21.createElement("td", null, texture.path), /* @__PURE__ */ React21.createElement("td", null, texture.width && texture.height ? `${texture.width}\xD7${texture.height}` : "-"), /* @__PURE__ */ React21.createElement("td", null, texture.purpose || "-"))))));
6091
+ var ParametersTab = ({ effect }) => /* @__PURE__ */ React21.createElement("div", { className: "fx-tab-parameters" }, /* @__PURE__ */ React21.createElement("table", { className: "fx-table" }, /* @__PURE__ */ React21.createElement("thead", null, /* @__PURE__ */ React21.createElement("tr", null, /* @__PURE__ */ React21.createElement("th", null, "\u540D\u79F0"), /* @__PURE__ */ React21.createElement("th", null, "\u7C7B\u578B"), /* @__PURE__ */ React21.createElement("th", null, "\u8BED\u4E49"), /* @__PURE__ */ React21.createElement("th", null, "\u9ED8\u8BA4\u503C"), /* @__PURE__ */ React21.createElement("th", null, "\u884C\u53F7"))), /* @__PURE__ */ React21.createElement("tbody", null, effect.parameters.map((param, idx) => /* @__PURE__ */ React21.createElement("tr", { key: idx }, /* @__PURE__ */ React21.createElement("td", null, /* @__PURE__ */ React21.createElement("code", null, param.name)), /* @__PURE__ */ React21.createElement("td", null, /* @__PURE__ */ React21.createElement("code", null, param.type)), /* @__PURE__ */ React21.createElement("td", null, param.semantic || "-"), /* @__PURE__ */ React21.createElement("td", null, param.defaultValue || "-"), /* @__PURE__ */ React21.createElement("td", null, param.lineNumber))))));
6092
+ var ValidationTab = ({ validation }) => /* @__PURE__ */ React21.createElement("div", { className: "fx-tab-validation" }, /* @__PURE__ */ React21.createElement("div", { className: `fx-validation-status ${validation.isValid ? "valid" : "invalid"}` }, /* @__PURE__ */ React21.createElement("h3", null, validation.isValid ? "\u2713 \u9A8C\u8BC1\u901A\u8FC7" : "\u2717 \u9A8C\u8BC1\u5931\u8D25")), validation.errors.length > 0 && /* @__PURE__ */ React21.createElement("div", { className: "fx-validation-section fx-errors" }, /* @__PURE__ */ React21.createElement("h4", null, "\u9519\u8BEF (", validation.errors.length, ")"), /* @__PURE__ */ React21.createElement("ul", null, validation.errors.map((error, idx) => /* @__PURE__ */ React21.createElement("li", { key: idx, className: "fx-error-item" }, error)))), validation.warnings.length > 0 && /* @__PURE__ */ React21.createElement("div", { className: "fx-validation-section fx-warnings" }, /* @__PURE__ */ React21.createElement("h4", null, "\u8B66\u544A (", validation.warnings.length, ")"), /* @__PURE__ */ React21.createElement("ul", null, validation.warnings.map((warning, idx) => /* @__PURE__ */ React21.createElement("li", { key: idx, className: "fx-warning-item" }, warning)))), validation.isValid && validation.warnings.length === 0 && /* @__PURE__ */ React21.createElement("p", { className: "fx-success-message" }, "FX\u6587\u4EF6\u7ED3\u6784\u5B8C\u6574\uFF0C\u6CA1\u6709\u53D1\u73B0\u95EE\u9898\u3002"));
6093
+ FXViewer.displayName = "FXViewer";
6094
+ var FXThreePreview = ({
6095
+ effect,
6096
+ texturePath = "",
6097
+ objectType = "sphere",
6098
+ className = "",
6099
+ showInfo = true
6100
+ }) => {
6101
+ const containerRef = useRef(null);
6102
+ const sceneRef = useRef(null);
6103
+ const rendererRef = useRef(null);
6104
+ const cameraRef = useRef(null);
6105
+ const controlsRef = useRef(null);
6106
+ const animationIdRef = useRef(null);
6107
+ const meshRef = useRef(null);
6108
+ const [loading, setLoading] = useState(true);
6109
+ const [error, setError] = useState(null);
6110
+ const [adapterInfo, setAdapterInfo] = useState(null);
6111
+ useEffect(() => {
6112
+ if (!containerRef.current) return;
6113
+ const init = async () => {
6114
+ try {
6115
+ setLoading(true);
6116
+ setError(null);
6117
+ const container = containerRef.current;
6118
+ const width = container.clientWidth || 600;
6119
+ const height = container.clientHeight || 400;
6120
+ const scene = new THREE3.Scene();
6121
+ scene.background = new THREE3.Color(1710638);
6122
+ sceneRef.current = scene;
6123
+ const camera = new THREE3.PerspectiveCamera(45, width / height, 0.1, 1e3);
6124
+ camera.position.set(0, 2, 5);
6125
+ cameraRef.current = camera;
6126
+ const renderer = new THREE3.WebGLRenderer({ antialias: true });
6127
+ renderer.setSize(width, height);
6128
+ renderer.setPixelRatio(window.devicePixelRatio);
6129
+ container.appendChild(renderer.domElement);
6130
+ rendererRef.current = renderer;
6131
+ const controls = new OrbitControls(camera, renderer.domElement);
6132
+ controls.enableDamping = true;
6133
+ controls.dampingFactor = 0.05;
6134
+ controlsRef.current = controls;
6135
+ const adapter = new FXToThreeAdapter(effect, texturePath);
6136
+ console.log("Loading textures...");
6137
+ await adapter.loadTextures();
6138
+ adapter.configureScene(scene, renderer);
6139
+ let geometry;
6140
+ switch (objectType) {
6141
+ case "box":
6142
+ geometry = new THREE3.BoxGeometry(2, 2, 2);
6143
+ break;
6144
+ case "torus":
6145
+ geometry = new THREE3.TorusGeometry(1, 0.4, 16, 100);
6146
+ break;
6147
+ case "plane":
6148
+ geometry = new THREE3.PlaneGeometry(3, 3);
6149
+ break;
6150
+ default:
6151
+ geometry = new THREE3.SphereGeometry(1.5, 32, 32);
6152
+ }
6153
+ const material = adapter.createMaterial();
6154
+ const mesh = new THREE3.Mesh(geometry, material);
6155
+ mesh.castShadow = true;
6156
+ mesh.receiveShadow = true;
6157
+ scene.add(mesh);
6158
+ meshRef.current = mesh;
6159
+ const renderConfig = adapter.extractRenderConfig();
6160
+ if (renderConfig.enableShadow) {
6161
+ const groundGeometry = new THREE3.PlaneGeometry(10, 10);
6162
+ const groundMaterial = new THREE3.MeshStandardMaterial({ color: 2763326 });
6163
+ const ground = new THREE3.Mesh(groundGeometry, groundMaterial);
6164
+ ground.rotation.x = -Math.PI / 2;
6165
+ ground.position.y = -2;
6166
+ ground.receiveShadow = true;
6167
+ scene.add(ground);
6168
+ }
6169
+ setAdapterInfo(adapter.getSummary());
6170
+ const animate = () => {
6171
+ animationIdRef.current = requestAnimationFrame(animate);
6172
+ if (meshRef.current) {
6173
+ meshRef.current.rotation.y += 5e-3;
6174
+ }
6175
+ if (controlsRef.current) {
6176
+ controlsRef.current.update();
6177
+ }
6178
+ if (rendererRef.current && sceneRef.current && cameraRef.current) {
6179
+ rendererRef.current.render(sceneRef.current, cameraRef.current);
6180
+ }
6181
+ };
6182
+ animate();
6183
+ setLoading(false);
6184
+ } catch (err) {
6185
+ console.error("FX Preview initialization error:", err);
6186
+ setError(err instanceof Error ? err.message : "\u521D\u59CB\u5316\u5931\u8D25");
6187
+ setLoading(false);
6188
+ }
6189
+ };
6190
+ init();
6191
+ return () => {
6192
+ if (animationIdRef.current) {
6193
+ cancelAnimationFrame(animationIdRef.current);
6194
+ }
6195
+ if (rendererRef.current) {
6196
+ rendererRef.current.dispose();
6197
+ if (containerRef.current && rendererRef.current.domElement) {
6198
+ containerRef.current.removeChild(rendererRef.current.domElement);
6199
+ }
6200
+ }
6201
+ if (sceneRef.current) {
6202
+ sceneRef.current.traverse((obj) => {
6203
+ if (obj instanceof THREE3.Mesh) {
6204
+ obj.geometry?.dispose();
6205
+ if (obj.material) {
6206
+ if (Array.isArray(obj.material)) {
6207
+ obj.material.forEach((m) => m.dispose());
6208
+ } else {
6209
+ obj.material.dispose();
6210
+ }
6211
+ }
6212
+ }
6213
+ });
6214
+ }
6215
+ };
6216
+ }, [effect, texturePath, objectType]);
6217
+ if (loading) {
6218
+ return /* @__PURE__ */ React21.createElement("div", { className: `fx-three-preview loading ${className}` }, /* @__PURE__ */ React21.createElement("div", { className: "preview-loading" }, "\u52A0\u8F7D\u4E2D..."));
6219
+ }
6220
+ if (error) {
6221
+ return /* @__PURE__ */ React21.createElement("div", { className: `fx-three-preview error ${className}` }, /* @__PURE__ */ React21.createElement("div", { className: "preview-error" }, /* @__PURE__ */ React21.createElement("h3", null, "\u274C \u6E32\u67D3\u5931\u8D25"), /* @__PURE__ */ React21.createElement("p", null, error)));
6222
+ }
6223
+ return /* @__PURE__ */ React21.createElement("div", { className: `fx-three-preview ${className}` }, /* @__PURE__ */ React21.createElement(
6224
+ "div",
6225
+ {
6226
+ ref: containerRef,
6227
+ className: "preview-container",
6228
+ style: {
6229
+ width: "100%",
6230
+ height: "100%",
6231
+ minHeight: "400px",
6232
+ position: "relative"
6233
+ }
6234
+ }
6235
+ ), showInfo && adapterInfo && /* @__PURE__ */ React21.createElement("div", { className: "preview-info" }, /* @__PURE__ */ React21.createElement("div", { className: "info-section" }, /* @__PURE__ */ React21.createElement("h4", null, "\u{1F3A8} \u5E94\u7528\u7684FX\u53C2\u6570"), /* @__PURE__ */ React21.createElement("div", { className: "info-badges" }, /* @__PURE__ */ React21.createElement("span", { className: "badge" }, adapterInfo.materialParams.length, " \u4E2A\u6750\u8D28\u53C2\u6570"), /* @__PURE__ */ React21.createElement("span", { className: "badge" }, adapterInfo.textures.length, " \u4E2A\u7EB9\u7406"), /* @__PURE__ */ React21.createElement("span", { className: "badge" }, adapterInfo.renderFeatures.length, " \u4E2A\u6E32\u67D3\u7279\u6027"))), adapterInfo.renderFeatures.length > 0 && /* @__PURE__ */ React21.createElement("div", { className: "info-section" }, /* @__PURE__ */ React21.createElement("h5", null, "\u542F\u7528\u7684\u7279\u6027:"), /* @__PURE__ */ React21.createElement("div", { className: "feature-list" }, adapterInfo.renderFeatures.map((feature, idx) => /* @__PURE__ */ React21.createElement("span", { key: idx, className: "feature-tag" }, feature))))), /* @__PURE__ */ React21.createElement("style", null, `
6236
+ .fx-three-preview {
6237
+ display: flex;
6238
+ flex-direction: column;
6239
+ gap: 1rem;
6240
+ }
6241
+
6242
+ .fx-three-preview.loading,
6243
+ .fx-three-preview.error {
6244
+ min-height: 400px;
6245
+ display: flex;
6246
+ align-items: center;
6247
+ justify-content: center;
6248
+ background: #f5f5f5;
6249
+ border-radius: 8px;
6250
+ }
6251
+
6252
+ .preview-loading {
6253
+ font-size: 1.1rem;
6254
+ color: #666;
6255
+ }
6256
+
6257
+ .preview-error {
6258
+ text-align: center;
6259
+ color: #d32f2f;
6260
+ padding: 2rem;
6261
+ }
6262
+
6263
+ .preview-error h3 {
6264
+ margin: 0 0 1rem;
6265
+ }
6266
+
6267
+ .preview-container {
6268
+ border-radius: 8px;
6269
+ overflow: hidden;
6270
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
6271
+ }
6272
+
6273
+ .preview-info {
6274
+ background: white;
6275
+ border-radius: 8px;
6276
+ padding: 1.5rem;
6277
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
6278
+ }
6279
+
6280
+ .info-section {
6281
+ margin-bottom: 1rem;
6282
+ }
6283
+
6284
+ .info-section:last-child {
6285
+ margin-bottom: 0;
6286
+ }
6287
+
6288
+ .info-section h4 {
6289
+ margin: 0 0 0.75rem;
6290
+ font-size: 1rem;
6291
+ color: #333;
6292
+ }
6293
+
6294
+ .info-section h5 {
6295
+ margin: 0 0 0.5rem;
6296
+ font-size: 0.9rem;
6297
+ color: #666;
6298
+ }
6299
+
6300
+ .info-badges {
6301
+ display: flex;
6302
+ flex-wrap: wrap;
6303
+ gap: 0.5rem;
6304
+ }
6305
+
6306
+ .badge {
6307
+ display: inline-block;
6308
+ padding: 0.4rem 0.8rem;
6309
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
6310
+ color: white;
6311
+ border-radius: 16px;
6312
+ font-size: 0.85rem;
6313
+ font-weight: 500;
6314
+ }
6315
+
6316
+ .feature-list {
6317
+ display: flex;
6318
+ flex-wrap: wrap;
6319
+ gap: 0.5rem;
6320
+ }
6321
+
6322
+ .feature-tag {
6323
+ display: inline-block;
6324
+ padding: 0.3rem 0.7rem;
6325
+ background: #e8f5e9;
6326
+ color: #2e7d32;
6327
+ border-radius: 12px;
6328
+ font-size: 0.8rem;
6329
+ font-weight: 500;
6330
+ }
6331
+ `));
6332
+ };
6333
+ FXThreePreview.displayName = "FXThreePreview";
6334
+
6335
+ export { ChoiceMenu, DialogueBox, FXParser, FXThreePreview, FXToThreeAdapter, FXViewer, HLSLToGLSLConverter, HistoryPanel, LoadingOverlay, LoadingScreen, MMDARPlayer, MMDMusicPlayer, MMDPlayerBase, MMDPlayerEnhanced, MMDPlayerEnhancedDebugInfo, MMDPlaylist, MMDPlaylistDebugInfo, MMDVisualNovel, MultiFXAdapter, MusicControls, PlaylistPanel, StartScreen, TrackInfo, compareFXEffects, exportFXToJSON, exportFXToMarkdown, extractTexturePaths, filterDefinesByPrefix, getColorParameters, getConfigSummaryText, getFeatureFlags, getTextureDefines, hasFeature, loadAmmo, validateFXEffect };
4283
6336
  //# sourceMappingURL=index.mjs.map
4284
6337
  //# sourceMappingURL=index.mjs.map