@playcraft/build 0.0.4 → 0.0.9

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 (64) hide show
  1. package/dist/analyzers/scene-asset-collector.js +210 -1
  2. package/dist/base-builder.d.ts +15 -0
  3. package/dist/base-builder.js +192 -16
  4. package/dist/generators/config-generator.js +29 -3
  5. package/dist/loaders/playcanvas-loader.d.ts +7 -0
  6. package/dist/loaders/playcanvas-loader.js +53 -3
  7. package/dist/platforms/adikteev.d.ts +10 -0
  8. package/dist/platforms/adikteev.js +72 -0
  9. package/dist/platforms/base.d.ts +12 -0
  10. package/dist/platforms/base.js +208 -0
  11. package/dist/platforms/facebook.js +5 -2
  12. package/dist/platforms/index.d.ts +4 -0
  13. package/dist/platforms/index.js +16 -0
  14. package/dist/platforms/inmobi.d.ts +10 -0
  15. package/dist/platforms/inmobi.js +68 -0
  16. package/dist/platforms/ironsource.js +5 -2
  17. package/dist/platforms/moloco.js +5 -2
  18. package/dist/platforms/playcraft.d.ts +33 -0
  19. package/dist/platforms/playcraft.js +44 -0
  20. package/dist/platforms/remerge.d.ts +10 -0
  21. package/dist/platforms/remerge.js +56 -0
  22. package/dist/templates/__loading__.js +100 -0
  23. package/dist/templates/__modules__.js +47 -0
  24. package/dist/templates/__settings__.template.js +20 -0
  25. package/dist/templates/__start__.js +332 -0
  26. package/dist/templates/index.html +18 -0
  27. package/dist/templates/logo.png +0 -0
  28. package/dist/templates/manifest.json +1 -0
  29. package/dist/templates/patches/cannon.min.js +28 -0
  30. package/dist/templates/patches/lz4.js +10 -0
  31. package/dist/templates/patches/one-page-http-get.js +20 -0
  32. package/dist/templates/patches/one-page-inline-game-scripts.js +52 -0
  33. package/dist/templates/patches/one-page-mraid-resize-canvas.js +46 -0
  34. package/dist/templates/patches/p2.min.js +27 -0
  35. package/dist/templates/patches/playcraft-no-xhr.js +76 -0
  36. package/dist/templates/playcanvas-stable.min.js +16363 -0
  37. package/dist/templates/styles.css +43 -0
  38. package/dist/types.d.ts +14 -1
  39. package/dist/utils/build-mode-detector.d.ts +9 -0
  40. package/dist/utils/build-mode-detector.js +42 -0
  41. package/dist/vite/config-builder.d.ts +29 -1
  42. package/dist/vite/config-builder.js +169 -25
  43. package/dist/vite/platform-configs.d.ts +4 -0
  44. package/dist/vite/platform-configs.js +97 -13
  45. package/dist/vite/plugin-esm-html-generator.d.ts +22 -0
  46. package/dist/vite/plugin-esm-html-generator.js +1061 -0
  47. package/dist/vite/plugin-platform.js +56 -17
  48. package/dist/vite/plugin-playcanvas.d.ts +2 -0
  49. package/dist/vite/plugin-playcanvas.js +497 -40
  50. package/dist/vite/plugin-source-builder.d.ts +3 -0
  51. package/dist/vite/plugin-source-builder.js +886 -19
  52. package/dist/vite-builder.d.ts +19 -2
  53. package/dist/vite-builder.js +162 -12
  54. package/package.json +2 -1
  55. package/physics/cannon-es-bundle.js +13092 -0
  56. package/physics/cannon-rigidbody-adapter.js +375 -0
  57. package/physics/connon-integration.js +411 -0
  58. package/templates/__start__.js +8 -3
  59. package/templates/index.esm.html +20 -0
  60. package/templates/index.esm.mjs +502 -0
  61. package/templates/patches/one-page-inline-game-scripts.js +25 -1
  62. package/templates/patches/playcraft-cta-adapter.js +297 -0
  63. package/templates/patches/playcraft-no-xhr.js +25 -1
  64. package/templates/playcanvas-esm-wrapper.mjs +827 -0
@@ -0,0 +1,502 @@
1
+ // Import PlayCanvas Engine
2
+ import * as pc from './playcanvas-engine.mjs';
3
+
4
+ // Export for global access - create a mutable wrapper object
5
+ // ESM namespace objects are frozen, so we spread into a new object
6
+ window.pc = { ...pc };
7
+
8
+ // Configuration Constants
9
+ const ASSET_PREFIX = "{{ASSET_PREFIX}}";
10
+ const SCRIPT_PREFIX = "{{SCRIPT_PREFIX}}";
11
+ const SCENE_PATH = "{{SCENE_PATH}}";
12
+ const CONTEXT_OPTIONS = {{CONTEXT_OPTIONS}};
13
+ const SCRIPTS = {{SCRIPTS}};
14
+ const CONFIG_FILENAME = "config.json";
15
+ const INPUT_SETTINGS = {{INPUT_SETTINGS}};
16
+ const PRELOAD_MODULES = {{PRELOAD_MODULES}};
17
+ if (pc.script) {
18
+ pc.script.legacy = {{LEGACY_SCRIPTS}};
19
+ }
20
+
21
+ // Export configuration constants to window for compatibility with __start__.js
22
+ // This is needed when ESM code is bundled as IIFE for playable ads
23
+ window.ASSET_PREFIX = ASSET_PREFIX;
24
+ window.SCRIPT_PREFIX = SCRIPT_PREFIX;
25
+ window.SCENE_PATH = SCENE_PATH;
26
+ window.CONTEXT_OPTIONS = CONTEXT_OPTIONS;
27
+ window.SCRIPTS = SCRIPTS;
28
+ window.INPUT_SETTINGS = INPUT_SETTINGS;
29
+ window.PRELOAD_MODULES = PRELOAD_MODULES;
30
+
31
+ // Script imports will be loaded after app initialization
32
+ const SCRIPT_IMPORT_PATHS = {{SCRIPT_IMPORT_PATHS}};
33
+
34
+ // GameRule path from import map (resolved at build time for bundling)
35
+ const GAME_RULE_PATH = "{{GAME_RULE_PATH}}";
36
+
37
+ // WASM Module Loader
38
+ const loadModules = function(modules, urlPrefix) {
39
+ return new Promise(resolve => {
40
+ if (typeof modules === 'undefined' || modules.length === 0) {
41
+ resolve();
42
+ } else {
43
+ let remaining = modules.length;
44
+ const moduleLoaded = () => {
45
+ if (--remaining === 0) {
46
+ resolve();
47
+ }
48
+ };
49
+ modules.forEach(function(m) {
50
+ pc.WasmModule.setConfig(m.moduleName, {
51
+ glueUrl: urlPrefix + m.glueUrl,
52
+ wasmUrl: urlPrefix + m.wasmUrl,
53
+ fallbackUrl: urlPrefix + m.fallbackUrl
54
+ });
55
+ if (!m.hasOwnProperty('preload') || m.preload) {
56
+ if (m.moduleName === 'BASIS') {
57
+ pc.basisInitialize();
58
+ moduleLoaded();
59
+ } else if (m.moduleName === 'DracoDecoderModule') {
60
+ if (pc.dracoInitialize) {
61
+ pc.dracoInitialize();
62
+ moduleLoaded();
63
+ } else {
64
+ pc.WasmModule.getInstance(m.moduleName, moduleLoaded);
65
+ }
66
+ } else {
67
+ pc.WasmModule.getInstance(m.moduleName, moduleLoaded);
68
+ }
69
+ } else {
70
+ moduleLoaded();
71
+ }
72
+ });
73
+ }
74
+ });
75
+ };
76
+
77
+ // Store loaded modules globally for later GameRule initialization
78
+ let loadedScriptModules = new Map();
79
+ let scriptBaseUrl = '';
80
+
81
+ // Dynamic script loader - loads scripts after app is initialized
82
+ async function loadUserScripts(scriptPaths) {
83
+ if (!scriptPaths || scriptPaths.length === 0) {
84
+ return;
85
+ }
86
+ console.log(`[ESM] Loading ${scriptPaths.length} user scripts...`);
87
+
88
+ // Get the base URL of the current module
89
+ scriptBaseUrl = new URL('.', import.meta.url).href;
90
+ console.log(`[ESM] Base URL: ${scriptBaseUrl}`);
91
+
92
+ for (const scriptPath of scriptPaths) {
93
+ try {
94
+ // Resolve the script path relative to this module's location
95
+ const absoluteUrl = new URL(scriptPath, scriptBaseUrl).href;
96
+ console.log(`[ESM] Loading: ${scriptPath} -> ${absoluteUrl}`);
97
+ const module = await import(absoluteUrl);
98
+ loadedScriptModules.set(scriptPath, module);
99
+ console.log(`[ESM] Loaded: ${scriptPath}`);
100
+ } catch (e) {
101
+ console.error(`[ESM] Failed to load script: ${scriptPath}`, e);
102
+ }
103
+ }
104
+
105
+ // NOTE: Do NOT call initializeGameRule here!
106
+ // GameRule initialization must happen AFTER app.configure() completes,
107
+ // because app.root is not available until then.
108
+ // initializeGameRule is called in configure() after loadScene().
109
+ console.log('[ESM] Scripts loaded. GameRule will be initialized after scene loads.');
110
+ }
111
+
112
+ // Auto-initialize GameRule from loaded modules
113
+ // IMPORTANT: This must be called AFTER app.root is available (after loadScene)
114
+ async function initializeGameRule() {
115
+ try {
116
+ // Find GameplaySystem module
117
+ let GameplaySystem = null;
118
+ for (const [path, module] of loadedScriptModules) {
119
+ if (module.GameplaySystem) {
120
+ GameplaySystem = module.GameplaySystem;
121
+ console.log(`[ESM] Found GameplaySystem in: ${path}`);
122
+ break;
123
+ }
124
+ }
125
+
126
+ if (!GameplaySystem) {
127
+ console.log('[ESM] GameplaySystem not found in loaded modules');
128
+ return;
129
+ }
130
+
131
+ // Check if gameRule is already set
132
+ if (GameplaySystem.gameRule) {
133
+ console.log('[ESM] GameplaySystem.gameRule already initialized');
134
+ return;
135
+ }
136
+
137
+ // Try to load GameRule using resolved path (not bare specifier for bundling compatibility)
138
+ // The path is resolved from import map at build time
139
+ if (!GAME_RULE_PATH) {
140
+ console.log('[ESM] No gameRule path configured');
141
+ return;
142
+ }
143
+
144
+ try {
145
+ // Use relative path for dynamic import (bundler-friendly)
146
+ const absoluteUrl = new URL(GAME_RULE_PATH, scriptBaseUrl).href;
147
+ console.log(`[ESM] Loading GameRule from: ${absoluteUrl}`);
148
+ const gameRuleModule = await import(absoluteUrl);
149
+
150
+ if (gameRuleModule.GameRule) {
151
+ GameplaySystem.gameRule = new gameRuleModule.GameRule();
152
+ console.log(`[ESM] Auto-initialized GameplaySystem.gameRule: ${GameplaySystem.gameRule.ruleName || 'GameRule'}`);
153
+ }
154
+ } catch (e) {
155
+ // GameRule module not found or doesn't export GameRule class
156
+ console.log('[ESM] Could not auto-initialize GameRule:', e.message);
157
+ }
158
+ } catch (e) {
159
+ console.error('[ESM] Error in initializeGameRule:', e);
160
+ }
161
+ }
162
+
163
+ // Canvas Setup
164
+ const CANVAS_ID = 'application-canvas';
165
+
166
+ const getIosVersion = () => {
167
+ if (/iP(hone|od|ad)/.test(navigator.platform)) {
168
+ const v = navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/);
169
+ return [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)];
170
+ }
171
+ return null;
172
+ };
173
+
174
+ let lastWindowHeight = window.innerHeight;
175
+ let lastWindowWidth = window.innerWidth;
176
+ let windowSizeChangeIntervalHandler = null;
177
+
178
+ const pcBootstrap = {
179
+ reflowHandler: null,
180
+ iosVersion: getIosVersion(),
181
+ createCanvas: function() {
182
+ const canvas = document.createElement('canvas');
183
+ canvas.setAttribute('id', CANVAS_ID);
184
+ canvas.setAttribute('tabindex', 0);
185
+ canvas.onselectstart = function() { return false; };
186
+ canvas.style['-webkit-user-select'] = 'none';
187
+ document.body.appendChild(canvas);
188
+ return canvas;
189
+ },
190
+ resizeCanvas: function(app, canvas) {
191
+ canvas.style.width = '';
192
+ canvas.style.height = '';
193
+ app.resizeCanvas(canvas.width, canvas.height);
194
+ const fillMode = app._fillMode;
195
+ if (fillMode === pc.FILLMODE_NONE || fillMode === pc.FILLMODE_KEEP_ASPECT) {
196
+ if ((fillMode === pc.FILLMODE_NONE && canvas.clientHeight < window.innerHeight) ||
197
+ canvas.clientWidth / canvas.clientHeight >= window.innerWidth / window.innerHeight) {
198
+ canvas.style.marginTop = Math.floor((window.innerHeight - canvas.clientHeight) / 2) + 'px';
199
+ } else {
200
+ canvas.style.marginTop = '';
201
+ }
202
+ }
203
+ lastWindowHeight = window.innerHeight;
204
+ lastWindowWidth = window.innerWidth;
205
+ if (this.iosVersion && this.iosVersion[0] <= 12) {
206
+ window.scrollTo(0, 0);
207
+ }
208
+ },
209
+ reflow: function(app, canvas) {
210
+ this.resizeCanvas(app, canvas);
211
+ if (windowSizeChangeIntervalHandler === null) {
212
+ windowSizeChangeIntervalHandler = setInterval(() => {
213
+ if (lastWindowHeight !== window.innerHeight || lastWindowWidth !== window.innerWidth) {
214
+ this.resizeCanvas(app, canvas);
215
+ }
216
+ }, 100);
217
+ setTimeout(function() {
218
+ if (!!windowSizeChangeIntervalHandler) {
219
+ clearInterval(windowSizeChangeIntervalHandler);
220
+ windowSizeChangeIntervalHandler = null;
221
+ }
222
+ }, 2000);
223
+ }
224
+ }
225
+ };
226
+
227
+ window.pcBootstrap = pcBootstrap;
228
+
229
+ const canvas = pcBootstrap.createCanvas();
230
+ const app = new pc.AppBase(canvas);
231
+
232
+ // Make app accessible via pc.app for scripts that use it
233
+ // Note: We use window.pc (the original mutable object) instead of the imported pc module
234
+ // because ES module namespace objects are frozen and cannot have properties added
235
+ if (window.pc) {
236
+ window.pc.app = app;
237
+ console.log('[ESM] window.pc.app has been set:', !!window.pc.app);
238
+ } else {
239
+ console.error('[ESM] window.pc is not available!');
240
+ }
241
+
242
+ function initCSS() {
243
+ if (document.head.querySelector) {
244
+ var css = `@media screen and (min-aspect-ratio: ${app._width}/${app._height}) {
245
+ #application-canvas.fill-mode-KEEP_ASPECT {
246
+ width: auto;
247
+ height: 100%;
248
+ margin: 0 auto;
249
+ }
250
+ }`;
251
+ document.head.querySelector('style').innerHTML += css;
252
+ }
253
+ if (canvas.classList) {
254
+ canvas.classList.add(`fill-mode-${app.fillMode}`);
255
+ }
256
+ }
257
+
258
+ function displayError(html) {
259
+ const div = document.createElement('div');
260
+ div.innerHTML = `<table style="background-color: #8CE; width: 100%; height: 100%;">
261
+ <tr>
262
+ <td align="center">
263
+ <div style="display: table-cell; vertical-align: middle;">
264
+ <div style="">${html}</div>
265
+ </div>
266
+ </td>
267
+ </tr>
268
+ </table>`;
269
+ document.body.appendChild(div);
270
+ }
271
+
272
+ function createLocalGraphicsDevice() {
273
+ const deviceOptions = CONTEXT_OPTIONS ?? {};
274
+ var LEGACY_WEBGL = 'webgl';
275
+ // 确保 deviceTypes 存在,如果不存在则使用默认值
276
+ var deviceTypes = deviceOptions.deviceTypes
277
+ ? [...deviceOptions.deviceTypes, LEGACY_WEBGL]
278
+ : ['webgl2', 'webgl1', LEGACY_WEBGL];
279
+ const gpuLibPath = '';
280
+ const gfxOptions = {
281
+ deviceTypes: deviceTypes,
282
+ glslangUrl: gpuLibPath + 'glslang.js',
283
+ twgslUrl: gpuLibPath + 'twgsl.js',
284
+ powerPreference: deviceOptions.powerPreference,
285
+ antialias: deviceOptions.antialias !== false,
286
+ alpha: deviceOptions.alpha === true,
287
+ preserveDrawingBuffer: !!deviceOptions.preserveDrawingBuffer
288
+ };
289
+ return pc.createGraphicsDevice(canvas, gfxOptions);
290
+ }
291
+
292
+ function initApp(device) {
293
+ try {
294
+ var createOptions = new pc.AppOptions();
295
+ createOptions.graphicsDevice = device;
296
+ createOptions.componentSystems = [
297
+ pc.RigidBodyComponentSystem, pc.CollisionComponentSystem, pc.JointComponentSystem,
298
+ pc.AnimationComponentSystem, pc.AnimComponentSystem, pc.ModelComponentSystem,
299
+ pc.RenderComponentSystem, pc.CameraComponentSystem, pc.LightComponentSystem,
300
+ pc.ScriptComponentSystem, pc.SoundComponentSystem, pc.AudioListenerComponentSystem,
301
+ pc.ParticleSystemComponentSystem, pc.ScreenComponentSystem, pc.ElementComponentSystem,
302
+ pc.ButtonComponentSystem, pc.ScrollViewComponentSystem, pc.ScrollbarComponentSystem,
303
+ pc.SpriteComponentSystem, pc.LayoutGroupComponentSystem, pc.LayoutChildComponentSystem,
304
+ pc.ZoneComponentSystem, pc.GSplatComponentSystem
305
+ ];
306
+ createOptions.resourceHandlers = [
307
+ pc.RenderHandler, pc.AnimationHandler, pc.AnimClipHandler, pc.AnimStateGraphHandler,
308
+ pc.ModelHandler, pc.MaterialHandler, pc.TextureHandler, pc.TextHandler,
309
+ pc.JsonHandler, pc.AudioHandler, pc.ScriptHandler, pc.SceneHandler,
310
+ pc.CubemapHandler, pc.HtmlHandler, pc.CssHandler, pc.ShaderHandler,
311
+ pc.HierarchyHandler, pc.FolderHandler, pc.FontHandler, pc.BinaryHandler,
312
+ pc.TextureAtlasHandler, pc.SpriteHandler, pc.TemplateHandler, pc.ContainerHandler,
313
+ pc.GSplatHandler
314
+ ];
315
+ createOptions.elementInput = new pc.ElementInput(canvas, {
316
+ useMouse: INPUT_SETTINGS.useMouse,
317
+ useTouch: INPUT_SETTINGS.useTouch
318
+ });
319
+ createOptions.keyboard = INPUT_SETTINGS.useKeyboard ? new pc.Keyboard(window) : null;
320
+ createOptions.mouse = INPUT_SETTINGS.useMouse ? new pc.Mouse(canvas) : null;
321
+ createOptions.gamepads = INPUT_SETTINGS.useGamepads ? new pc.GamePads() : null;
322
+ createOptions.touch = INPUT_SETTINGS.useTouch && pc.platform.touch ? new pc.TouchDevice(canvas) : null;
323
+ createOptions.assetPrefix = ASSET_PREFIX ?? '';
324
+ createOptions.scriptPrefix = SCRIPT_PREFIX ?? '';
325
+ createOptions.scriptsOrder = SCRIPTS ?? [];
326
+ createOptions.soundManager = new pc.SoundManager();
327
+ createOptions.lightmapper = pc.Lightmapper;
328
+ createOptions.batchManager = pc.BatchManager;
329
+ createOptions.xr = pc.XrManager;
330
+ app.init(createOptions);
331
+
332
+ // Execute deferred ESM Script registrations (if any)
333
+ // This happens after app.init() so pc.registerScript can work properly
334
+ if (window.__deferredESMScripts) {
335
+ console.log('[ESM] Executing ' + window.__deferredESMScripts.length + ' deferred ESM script registrations...');
336
+ window.__deferredESMScripts.forEach(function(fn) {
337
+ try {
338
+ fn();
339
+ } catch (e) {
340
+ console.error('[ESM] Deferred script registration error:', e);
341
+ }
342
+ });
343
+ window.__deferredESMScripts = null;
344
+ }
345
+
346
+ return true;
347
+ } catch (e) {
348
+ displayError('Could not initialize application. Error: ' + e);
349
+ console.error(e);
350
+ return false;
351
+ }
352
+ }
353
+
354
+ function configure() {
355
+ app.configure(CONFIG_FILENAME, err => {
356
+ if (err) {
357
+ console.error(err);
358
+ return;
359
+ }
360
+ initCSS();
361
+ setTimeout(() => {
362
+ pcBootstrap.reflow(app, canvas);
363
+ pcBootstrap.reflowHandler = function() {
364
+ pcBootstrap.reflow(app, canvas);
365
+ };
366
+ window.addEventListener('resize', pcBootstrap.reflowHandler, false);
367
+ window.addEventListener('orientationchange', pcBootstrap.reflowHandler, false);
368
+ app.preload(() => {
369
+ app.scenes.loadScene(SCENE_PATH, async err => {
370
+ if (err) {
371
+ console.error(err);
372
+ return;
373
+ }
374
+
375
+ // Initialize GameRule AFTER scene is loaded (app.root is now available)
376
+ // This fixes: "Cannot read properties of undefined (reading 'root')"
377
+ console.log('[ESM] Scene loaded, app.root is now available:', !!app.root);
378
+ await initializeGameRule();
379
+
380
+ app.start();
381
+ });
382
+ });
383
+ });
384
+ });
385
+ }
386
+
387
+ async function main() {
388
+ try {
389
+ const device = await createLocalGraphicsDevice();
390
+ if (initApp(device)) {
391
+ // Load WASM modules
392
+ await loadModules(PRELOAD_MODULES, ASSET_PREFIX);
393
+
394
+ // Load user scripts AFTER app.init() - this is critical for createScript to work
395
+ await loadUserScripts(SCRIPT_IMPORT_PATHS);
396
+
397
+ // Configure and start
398
+ configure();
399
+ }
400
+ } catch (e) {
401
+ console.error('Device creation error:', e);
402
+ }
403
+ }
404
+
405
+ // Loading Screen
406
+ pc.script.createLoadingScreen(app => {
407
+ const createCss = () => {
408
+ const css = `
409
+ body {
410
+ background-color: #283538;
411
+ }
412
+
413
+ #application-splash-wrapper {
414
+ position: absolute;
415
+ top: 0;
416
+ left: 0;
417
+ height: 100%;
418
+ width: 100%;
419
+ background-color: #283538;
420
+ }
421
+
422
+ #application-splash {
423
+ position: absolute;
424
+ top: calc(50% - 28px);
425
+ width: 264px;
426
+ left: calc(50% - 132px);
427
+ }
428
+
429
+ #application-splash img {
430
+ width: 100%;
431
+ }
432
+
433
+ #progress-bar-container {
434
+ margin: 20px auto 0 auto;
435
+ height: 2px;
436
+ width: 100%;
437
+ background-color: #1d292c;
438
+ }
439
+
440
+ #progress-bar {
441
+ width: 0%;
442
+ height: 100%;
443
+ background-color: #f60;
444
+ }
445
+
446
+ @media (max-width: 480px) {
447
+ #application-splash {
448
+ width: 170px;
449
+ left: calc(50% - 85px);
450
+ }
451
+ }
452
+ `;
453
+ const style = document.createElement('style');
454
+ style.textContent = css;
455
+ document.head.appendChild(style);
456
+ };
457
+
458
+ const showSplash = () => {
459
+ const wrapper = document.createElement('div');
460
+ wrapper.id = 'application-splash-wrapper';
461
+ document.body.appendChild(wrapper);
462
+ const splash = document.createElement('div');
463
+ splash.id = 'application-splash';
464
+ wrapper.appendChild(splash);
465
+ splash.style.display = 'none';
466
+ const logo = document.createElement('img');
467
+ logo.src = `${ASSET_PREFIX}logo.png`;
468
+ splash.appendChild(logo);
469
+ logo.onload = () => {
470
+ splash.style.display = 'block';
471
+ };
472
+ const container = document.createElement('div');
473
+ container.id = 'progress-bar-container';
474
+ splash.appendChild(container);
475
+ const bar = document.createElement('div');
476
+ bar.id = 'progress-bar';
477
+ container.appendChild(bar);
478
+ };
479
+
480
+ const setProgress = value => {
481
+ const bar = document.getElementById('progress-bar');
482
+ if (bar) {
483
+ value = Math.min(1, Math.max(0, value));
484
+ bar.style.width = `${value * 100}%`;
485
+ }
486
+ };
487
+
488
+ const hideSplash = () => {
489
+ document.getElementById('application-splash-wrapper').remove();
490
+ };
491
+
492
+ createCss();
493
+ showSplash();
494
+ app.on('preload:end', () => {
495
+ app.off('preload:progress');
496
+ });
497
+ app.on('preload:progress', setProgress);
498
+ app.on('start', hideSplash);
499
+ });
500
+
501
+ // Start the application
502
+ main();
@@ -18,7 +18,31 @@
18
18
  // Decode the url from base64 to text
19
19
  var index = url.indexOf(',');
20
20
  var base64 = url.slice(index + 1);
21
- var data = window.atob(base64);
21
+
22
+ // 移除查询参数(如 ?t=hash 用于缓存破坏)
23
+ var queryIndex = base64.indexOf('?');
24
+ if (queryIndex !== -1) {
25
+ base64 = base64.substring(0, queryIndex);
26
+ }
27
+
28
+ // 防御性检查:清理 base64 字符串
29
+ base64 = base64.trim().replace(/\s/g, '');
30
+
31
+ // 如果 base64 为空,跳过加载
32
+ if (!base64) {
33
+ console.warn('警告: 脚本 base64 数据为空,URL:', url.substring(0, 100));
34
+ callback('base64 数据为空');
35
+ return;
36
+ }
37
+
38
+ var data;
39
+ try {
40
+ data = window.atob(base64);
41
+ } catch (e) {
42
+ console.error('脚本 atob 解码失败:', e.message, 'URL:', url.substring(0, 100));
43
+ callback('atob 解码失败: ' + e.message);
44
+ return;
45
+ }
22
46
 
23
47
  element.innerText = data;
24
48
  head.appendChild(element);