underpost 3.2.4 → 3.2.8

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 (141) hide show
  1. package/.github/workflows/release.cd.yml +1 -2
  2. package/CHANGELOG.md +268 -1
  3. package/CLI-HELP.md +26 -13
  4. package/Dockerfile +0 -4
  5. package/README.md +3 -3
  6. package/bin/build.js +13 -3
  7. package/bin/deploy.js +570 -1
  8. package/bin/file.js +5 -0
  9. package/conf.js +11 -2
  10. package/jsconfig.json +1 -1
  11. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -3
  12. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +2 -3
  13. package/manifests/deployment/dd-default-development/deployment.yaml +2 -6
  14. package/manifests/deployment/dd-test-development/deployment.yaml +136 -66
  15. package/manifests/deployment/dd-test-development/proxy.yaml +41 -5
  16. package/package.json +20 -11
  17. package/src/api/core/core.controller.js +10 -10
  18. package/src/api/core/core.service.js +10 -10
  19. package/src/api/default/default.controller.js +10 -10
  20. package/src/api/default/default.service.js +10 -10
  21. package/src/api/document/document.controller.js +12 -12
  22. package/src/api/document/document.model.js +10 -16
  23. package/src/api/file/file.controller.js +8 -8
  24. package/src/api/file/file.model.js +10 -10
  25. package/src/api/file/file.service.js +36 -36
  26. package/src/api/test/test.controller.js +8 -8
  27. package/src/api/test/test.service.js +8 -8
  28. package/src/api/user/guest.service.js +99 -0
  29. package/src/api/user/user.controller.js +6 -6
  30. package/src/api/user/user.model.js +8 -13
  31. package/src/api/user/user.service.js +3 -20
  32. package/src/cli/deploy.js +33 -30
  33. package/src/cli/fs.js +62 -5
  34. package/src/cli/image.js +43 -1
  35. package/src/cli/index.js +5 -1
  36. package/src/cli/release.js +58 -2
  37. package/src/cli/repository.js +35 -3
  38. package/src/cli/run.js +304 -38
  39. package/src/cli/ssh.js +1 -1
  40. package/src/cli/static.js +43 -115
  41. package/src/client/Default.index.js +21 -33
  42. package/src/client/components/core/404.js +4 -4
  43. package/src/client/components/core/500.js +4 -4
  44. package/src/client/components/core/Account.js +73 -60
  45. package/src/client/components/core/AgGrid.js +23 -33
  46. package/src/client/components/core/Alert.js +12 -13
  47. package/src/client/components/core/AppStore.js +1 -1
  48. package/src/client/components/core/Auth.js +20 -32
  49. package/src/client/components/core/Badge.js +7 -13
  50. package/src/client/components/core/BtnIcon.js +15 -17
  51. package/src/client/components/core/CalendarCore.js +42 -63
  52. package/src/client/components/core/Chat.js +13 -15
  53. package/src/client/components/core/ClientEvents.js +87 -0
  54. package/src/client/components/core/ColorPaletteElement.js +309 -0
  55. package/src/client/components/core/Content.js +17 -14
  56. package/src/client/components/core/Css.js +15 -71
  57. package/src/client/components/core/CssCore.js +12 -16
  58. package/src/client/components/core/D3Chart.js +4 -4
  59. package/src/client/components/core/Docs.js +60 -59
  60. package/src/client/components/core/DropDown.js +69 -91
  61. package/src/client/components/core/EventBus.js +92 -0
  62. package/src/client/components/core/EventsUI.js +14 -17
  63. package/src/client/components/core/FileExplorer.js +102 -234
  64. package/src/client/components/core/FullScreen.js +47 -75
  65. package/src/client/components/core/Input.js +24 -69
  66. package/src/client/components/core/Keyboard.js +25 -18
  67. package/src/client/components/core/KeyboardAvoidance.js +145 -0
  68. package/src/client/components/core/LoadingAnimation.js +25 -31
  69. package/src/client/components/core/LogIn.js +41 -41
  70. package/src/client/components/core/LogOut.js +23 -14
  71. package/src/client/components/core/Modal.js +397 -176
  72. package/src/client/components/core/NotificationManager.js +14 -18
  73. package/src/client/components/core/Panel.js +54 -50
  74. package/src/client/components/core/PanelForm.js +25 -125
  75. package/src/client/components/core/Polyhedron.js +110 -214
  76. package/src/client/components/core/PublicProfile.js +39 -32
  77. package/src/client/components/core/Recover.js +52 -48
  78. package/src/client/components/core/Responsive.js +88 -32
  79. package/src/client/components/core/RichText.js +9 -18
  80. package/src/client/components/core/Router.js +24 -3
  81. package/src/client/components/core/SearchBox.js +37 -37
  82. package/src/client/components/core/SignUp.js +39 -30
  83. package/src/client/components/core/SocketIo.js +31 -2
  84. package/src/client/components/core/SocketIoHandler.js +6 -6
  85. package/src/client/components/core/ToggleSwitch.js +8 -20
  86. package/src/client/components/core/ToolTip.js +5 -17
  87. package/src/client/components/core/Translate.js +56 -59
  88. package/src/client/components/core/Validator.js +26 -16
  89. package/src/client/components/core/Wallet.js +15 -26
  90. package/src/client/components/core/Worker.js +140 -25
  91. package/src/client/components/core/windowGetDimensions.js +7 -7
  92. package/src/client/components/default/{MenuDefault.js → AppShellDefault.js} +87 -87
  93. package/src/client/components/default/CssDefault.js +12 -12
  94. package/src/client/components/default/LogInDefault.js +6 -4
  95. package/src/client/components/default/LogOutDefault.js +6 -4
  96. package/src/client/components/default/RouterDefault.js +47 -0
  97. package/src/client/components/default/SettingsDefault.js +4 -4
  98. package/src/client/components/default/SignUpDefault.js +6 -4
  99. package/src/client/components/default/TranslateDefault.js +3 -3
  100. package/src/client/services/core/core.service.js +17 -49
  101. package/src/client/services/default/default.management.js +139 -242
  102. package/src/client/services/default/default.service.js +10 -16
  103. package/src/client/services/document/document.service.js +14 -19
  104. package/src/client/services/file/file.service.js +8 -13
  105. package/src/client/services/test/test.service.js +8 -13
  106. package/src/client/services/user/guest.service.js +79 -0
  107. package/src/client/services/user/user.management.js +5 -5
  108. package/src/client/services/user/user.service.js +14 -20
  109. package/src/client/ssr/body/404.js +3 -3
  110. package/src/client/ssr/body/500.js +3 -3
  111. package/src/client/ssr/body/CacheControl.js +5 -2
  112. package/src/client/ssr/body/DefaultSplashScreen.js +19 -12
  113. package/src/client/ssr/mailer/DefaultRecoverEmail.js +19 -20
  114. package/src/client/ssr/mailer/DefaultVerifyEmail.js +15 -16
  115. package/src/client/ssr/offline/Maintenance.js +12 -11
  116. package/src/client/ssr/offline/NoNetworkConnection.js +3 -3
  117. package/src/client/ssr/pages/Test.js +2 -2
  118. package/src/client/sw/core.sw.js +212 -0
  119. package/src/index.js +1 -1
  120. package/src/runtime/express/Dockerfile +4 -4
  121. package/src/runtime/lampp/Dockerfile +8 -7
  122. package/src/runtime/wp/Dockerfile +11 -17
  123. package/src/server/backup.js +1 -2
  124. package/src/server/client-build-docs.js +45 -46
  125. package/src/server/client-build.js +334 -60
  126. package/src/server/client-formatted.js +47 -16
  127. package/src/server/conf.js +29 -13
  128. package/src/server/cron.js +6 -8
  129. package/src/server/dns.js +2 -1
  130. package/src/server/ipfs-client.js +232 -91
  131. package/src/server/process.js +13 -27
  132. package/src/server/start.js +6 -3
  133. package/src/server/valkey.js +134 -235
  134. package/tsconfig.docs.json +15 -0
  135. package/typedoc.json +20 -0
  136. package/jsdoc.json +0 -52
  137. package/src/client/components/core/ColorPalette.js +0 -5267
  138. package/src/client/components/core/JoyStick.js +0 -80
  139. package/src/client/components/default/RoutesDefault.js +0 -49
  140. package/src/client/sw/default.sw.js +0 -127
  141. package/src/client/sw/template.sw.js +0 -84
@@ -1,18 +1,16 @@
1
1
  // https://underpost.net/cube.php
2
-
3
2
  import { BtnIcon } from './BtnIcon.js';
4
3
  import { getId, random } from './CommonJs.js';
5
4
  import { dynamicCol } from './Css.js';
6
5
  import { fullScreenIn, htmls, s } from './VanillaJs.js';
7
6
  import { Translate } from './Translate.js';
8
7
  // https://css-loaders.com/3d/
9
-
10
- const Polyhedron = {
11
- Tokens: {},
12
- Render: async function (options) {
13
- const id = options?.id ? options.id : getId(this.Tokens, 'polyhedron-');
14
- if (!this.Tokens[id])
15
- this.Tokens[id] = {
8
+ class Polyhedron {
9
+ static Tokens = {};
10
+ static async instance(options) {
11
+ const id = options?.id ? options.id : getId(Polyhedron.Tokens, 'polyhedron-');
12
+ if (!Polyhedron.Tokens[id])
13
+ Polyhedron.Tokens[id] = {
16
14
  cr: [-25, -57, 90],
17
15
  ct: [0, 0, 0],
18
16
  dim: 150,
@@ -34,16 +32,13 @@ const Polyhedron = {
34
32
  bottom: null,
35
33
  },
36
34
  };
37
-
38
35
  const getFaceSelector = (faceName) => `.face_${faceName}-${id}`;
39
36
  const applyFaceBackground = (faceName) => {
40
37
  const el = s(getFaceSelector(faceName));
41
38
  if (!el) return;
42
- const url = this.Tokens[id].faces?.[faceName];
43
-
39
+ const url = Polyhedron.Tokens[id].faces?.[faceName];
44
40
  if (url) {
45
41
  el.style.backgroundImage = `url("${url}")`;
46
-
47
42
  // Always cover the full face area.
48
43
  // Using `cover` ensures full coverage even during 3D transforms / resizes.
49
44
  el.style.backgroundSize = 'cover';
@@ -59,31 +54,25 @@ const Polyhedron = {
59
54
  const applyAllFaceBackgrounds = () => {
60
55
  ['front', 'back', 'left', 'right', 'top', 'bottom'].forEach(applyFaceBackground);
61
56
  };
62
-
63
57
  const applyFaceOpacity = () => {
64
- const opacity = typeof this.Tokens[id].faceOpacity === 'number' ? this.Tokens[id].faceOpacity : 1;
58
+ const opacity = typeof Polyhedron.Tokens[id].faceOpacity === 'number' ? Polyhedron.Tokens[id].faceOpacity : 1;
65
59
  const faces = document.querySelectorAll(`.face-${id}`);
66
60
  faces.forEach((el) => {
67
61
  el.style.opacity = `${opacity}`;
68
62
  });
69
63
  };
70
-
71
64
  const startImmersiveEffects = () => {
72
65
  const scene = s(`.scene-${id}`);
73
66
  if (!scene) return;
74
-
75
67
  const canvas = s(`.polyhedron-immersive-canvas-${id}`);
76
68
  if (!canvas) return;
77
-
78
69
  if (!s(`.main-body-btn-ui-close`).classList.contains('hide')) s(`.main-body-btn-ui`).click();
79
70
  if (!s(`.main-body-btn-ui-menu-close`).classList.contains('hide')) s(`.main-body-btn-menu`).click();
80
71
  fullScreenIn();
81
72
  // Ensure canvas is visible during immersive mode.
82
73
  canvas.style.display = 'block';
83
-
84
74
  const ctx = canvas.getContext('2d');
85
75
  if (!ctx) return;
86
-
87
76
  const resize = () => {
88
77
  const dpr = Math.max(1, window.devicePixelRatio || 1);
89
78
  const w = scene.clientWidth || window.innerWidth;
@@ -94,9 +83,7 @@ const Polyhedron = {
94
83
  canvas.style.height = `${h}px`;
95
84
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
96
85
  };
97
-
98
86
  resize();
99
-
100
87
  const mkParticlesWaveLight = () => {
101
88
  const count = 110;
102
89
  const w = scene.clientWidth || window.innerWidth;
@@ -114,7 +101,6 @@ const Polyhedron = {
114
101
  };
115
102
  });
116
103
  };
117
-
118
104
  const mkParticlesDarkElectronic = () => {
119
105
  const count = 170;
120
106
  const w = scene.clientWidth || window.innerWidth;
@@ -135,7 +121,6 @@ const Polyhedron = {
135
121
  };
136
122
  });
137
123
  };
138
-
139
124
  // New (light): Prism Bloom — bright prismatic bursts with bloom + gently swirling motion
140
125
  const mkParticlesPrismBloom = () => {
141
126
  const count = 160;
@@ -157,7 +142,6 @@ const Polyhedron = {
157
142
  };
158
143
  });
159
144
  };
160
-
161
145
  // New (dark): Noir Embers — smoky dark with warm embers + occasional flicker
162
146
  const mkParticlesNoirEmbers = () => {
163
147
  // Repurposed into a much more eye-catching "full fire" effect:
@@ -185,42 +169,33 @@ const Polyhedron = {
185
169
  };
186
170
  });
187
171
  };
188
-
189
172
  const resetParticlesForEffect = () => {
190
- const eff = this.Tokens[id].immersiveEffect || 'waveLight';
173
+ const eff = Polyhedron.Tokens[id].immersiveEffect || 'waveLight';
191
174
  console.error(eff);
192
- if (eff === 'darkElectronic') this.Tokens[id].immersiveParticles = mkParticlesDarkElectronic();
193
- else if (eff === 'prismBloom') this.Tokens[id].immersiveParticles = mkParticlesPrismBloom();
194
- else if (eff === 'noirEmbers') this.Tokens[id].immersiveParticles = mkParticlesNoirEmbers();
195
- else this.Tokens[id].immersiveParticles = mkParticlesWaveLight();
175
+ if (eff === 'darkElectronic') Polyhedron.Tokens[id].immersiveParticles = mkParticlesDarkElectronic();
176
+ else if (eff === 'prismBloom') Polyhedron.Tokens[id].immersiveParticles = mkParticlesPrismBloom();
177
+ else if (eff === 'noirEmbers') Polyhedron.Tokens[id].immersiveParticles = mkParticlesNoirEmbers();
178
+ else Polyhedron.Tokens[id].immersiveParticles = mkParticlesWaveLight();
196
179
  };
197
-
198
180
  resetParticlesForEffect();
199
- this.Tokens[id].immersiveStart = performance.now();
200
-
181
+ Polyhedron.Tokens[id].immersiveStart = performance.now();
201
182
  const tick = (t) => {
202
- if (!this.Tokens[id].immersive) return;
203
-
183
+ if (!Polyhedron.Tokens[id].immersive) return;
204
184
  // Keep canvas sized to the scene.
205
185
  if (canvas.clientWidth !== scene.clientWidth || canvas.clientHeight !== scene.clientHeight) resize();
206
-
207
186
  const w2 = canvas.clientWidth || window.innerWidth;
208
187
  const h2 = canvas.clientHeight || window.innerHeight;
209
-
210
- const tt = (t - this.Tokens[id].immersiveStart) / 1000;
211
- const eff = this.Tokens[id].immersiveEffect || 'waveLight';
212
-
188
+ const tt = (t - Polyhedron.Tokens[id].immersiveStart) / 1000;
189
+ const eff = Polyhedron.Tokens[id].immersiveEffect || 'waveLight';
213
190
  if (eff === 'darkElectronic') {
214
191
  // Dark electronic: GREEN PIPES — grid/pipe network with bright green flow + pulsing nodes
215
192
  ctx.fillStyle = 'rgba(0,0,0,1)';
216
193
  ctx.fillRect(0, 0, w2, h2);
217
-
218
194
  // Pipe layout (more orthogonal so it reads like piping)
219
195
  const base = 120; // green hue
220
196
  const step = 52;
221
197
  const driftX = Math.sin(tt * 0.35) * 10;
222
198
  const driftY = Math.cos(tt * 0.28) * 10;
223
-
224
199
  // faint background glow to lift the pipes off black
225
200
  ctx.globalAlpha = 0.18;
226
201
  const bgG = ctx.createRadialGradient(w2 * 0.5, h2 * 0.5, 0, w2 * 0.5, h2 * 0.5, Math.min(w2, h2) * 0.95);
@@ -229,13 +204,11 @@ const Polyhedron = {
229
204
  ctx.fillStyle = bgG;
230
205
  ctx.fillRect(0, 0, w2, h2);
231
206
  ctx.globalAlpha = 1;
232
-
233
207
  // scanlines, very subtle
234
208
  ctx.globalAlpha = 0.04;
235
209
  ctx.fillStyle = 'rgba(255,255,255,1)';
236
210
  for (let y = 0; y < h2; y += 3) ctx.fillRect(0, y, w2, 1);
237
211
  ctx.globalAlpha = 1;
238
-
239
212
  // Pipes: horizontal lines
240
213
  ctx.lineWidth = 2;
241
214
  for (let y = 0; y <= h2 + step; y += step) {
@@ -247,7 +220,6 @@ const Polyhedron = {
247
220
  ctx.moveTo(0, yy);
248
221
  ctx.lineTo(w2, yy);
249
222
  ctx.stroke();
250
-
251
223
  // core pass
252
224
  ctx.globalAlpha = 0.35;
253
225
  ctx.strokeStyle = `hsla(${base}, 100%, 55%, 1)`;
@@ -257,7 +229,6 @@ const Polyhedron = {
257
229
  ctx.stroke();
258
230
  }
259
231
  ctx.globalAlpha = 1;
260
-
261
232
  // Pipes: vertical lines
262
233
  for (let x = 0; x <= w2 + step; x += step) {
263
234
  const xx = x + (Math.cos(tt * 0.55 + x * 0.02) * 2 + driftX);
@@ -268,7 +239,6 @@ const Polyhedron = {
268
239
  ctx.moveTo(xx, 0);
269
240
  ctx.lineTo(xx, h2);
270
241
  ctx.stroke();
271
-
272
242
  // core pass
273
243
  ctx.globalAlpha = 0.35;
274
244
  ctx.strokeStyle = `hsla(${base}, 100%, 55%, 1)`;
@@ -278,42 +248,34 @@ const Polyhedron = {
278
248
  ctx.stroke();
279
249
  }
280
250
  ctx.globalAlpha = 1;
281
-
282
251
  // Particles as flowing "packets" and junction nodes
283
- const ps = this.Tokens[id].immersiveParticles || [];
252
+ const ps = Polyhedron.Tokens[id].immersiveParticles || [];
284
253
  for (const p of ps) {
285
254
  // quantize positions toward pipe lanes so motion reads as "through pipes"
286
255
  const laneX = Math.round(p.x / step) * step;
287
256
  const laneY = Math.round(p.y / step) * step;
288
-
289
257
  // drift toward nearest lane
290
258
  p.x += (laneX - p.x) * 0.04;
291
259
  p.y += (laneY - p.y) * 0.04;
292
-
293
260
  // move slowly along lanes
294
261
  p.x += p.vx * 60;
295
262
  p.y += p.vy * 60;
296
-
297
263
  if (p.x < -20) p.x = w2 + 20;
298
264
  if (p.x > w2 + 20) p.x = -20;
299
265
  if (p.y < -20) p.y = h2 + 20;
300
266
  if (p.y > h2 + 20) p.y = -20;
301
-
302
267
  const sparkle = (0.25 + 0.75 * Math.max(0, Math.sin(tt * 5.2 + p.phase))) * (0.55 + p.spark);
303
-
304
268
  // packet glow
305
269
  ctx.beginPath();
306
270
  ctx.fillStyle = `hsla(${base}, 100%, 72%, ${0.08 + sparkle * 0.16})`;
307
271
  ctx.arc(p.x, p.y, p.r * 2.6, 0, Math.PI * 2);
308
272
  ctx.fill();
309
-
310
273
  // packet core
311
274
  ctx.beginPath();
312
275
  ctx.fillStyle = `hsla(${base + 10}, 100%, 62%, ${0.12 + sparkle * 0.22})`;
313
276
  ctx.arc(p.x, p.y, Math.max(1, p.r * 1.05), 0, Math.PI * 2);
314
277
  ctx.fill();
315
278
  }
316
-
317
279
  // Vignette
318
280
  const vg = ctx.createRadialGradient(
319
281
  w2 / 2,
@@ -327,19 +289,17 @@ const Polyhedron = {
327
289
  vg.addColorStop(1, 'rgba(0,0,0,0.75)');
328
290
  ctx.fillStyle = vg;
329
291
  ctx.fillRect(0, 0, w2, h2);
330
-
331
292
  // Polyhedron motion (calmer so the pipe aesthetic stays readable)
332
293
  const drift = 9;
333
- this.Tokens[id].cr[1] += 0.44;
334
- this.Tokens[id].cr[0] += 0.14;
335
- this.Tokens[id].cr[2] += 0.08;
336
- this.Tokens[id].ct[0] = Math.sin(tt * 0.65) * drift;
337
- this.Tokens[id].ct[1] = Math.cos(tt * 0.45) * drift;
338
- this.Tokens[id].ct[2] = Math.sin(tt * 0.35) * (drift * 0.65);
294
+ Polyhedron.Tokens[id].cr[1] += 0.44;
295
+ Polyhedron.Tokens[id].cr[0] += 0.14;
296
+ Polyhedron.Tokens[id].cr[2] += 0.08;
297
+ Polyhedron.Tokens[id].ct[0] = Math.sin(tt * 0.65) * drift;
298
+ Polyhedron.Tokens[id].ct[1] = Math.cos(tt * 0.45) * drift;
299
+ Polyhedron.Tokens[id].ct[2] = Math.sin(tt * 0.35) * (drift * 0.65);
339
300
  } else if (eff === 'prismBloom') {
340
301
  // Prism Bloom (light): COOL-CORES — soft light/white wave-lite style, slower particles
341
302
  const tt2 = tt;
342
-
343
303
  // cool white background with a gentle wave tint (very subtle blues)
344
304
  const bg = ctx.createLinearGradient(0, 0, w2, h2);
345
305
  bg.addColorStop(0, `hsla(205, 45%, 96%, 1)`);
@@ -347,7 +307,6 @@ const Polyhedron = {
347
307
  bg.addColorStop(1, `hsla(195, 40%, 95%, 1)`);
348
308
  ctx.fillStyle = bg;
349
309
  ctx.fillRect(0, 0, w2, h2);
350
-
351
310
  // soft wave bands
352
311
  ctx.globalAlpha = 0.12;
353
312
  ctx.lineWidth = 2;
@@ -363,36 +322,29 @@ const Polyhedron = {
363
322
  ctx.stroke();
364
323
  }
365
324
  ctx.globalAlpha = 1;
366
-
367
- const ps = this.Tokens[id].immersiveParticles || [];
325
+ const ps = Polyhedron.Tokens[id].immersiveParticles || [];
368
326
  for (const p of ps) {
369
327
  // slow drifting, wave-like
370
328
  p.x += (p.vx * 0.55 + Math.sin(tt2 * 0.55 + p.phase) * 0.06) * 60;
371
329
  p.y += (p.vy * 0.55 + Math.cos(tt2 * 0.45 + p.phase) * 0.06) * 60;
372
-
373
330
  if (p.x < -30) p.x = w2 + 30;
374
331
  if (p.x > w2 + 30) p.x = -30;
375
332
  if (p.y < -30) p.y = h2 + 30;
376
333
  if (p.y > h2 + 30) p.y = -30;
377
-
378
334
  const pulse = 0.35 + 0.65 * Math.max(0, Math.sin(tt2 * 1.5 + p.phase + p.jitter * 2));
379
-
380
335
  // cool core colors (icy blue -> lavender)
381
336
  const hueP = (205 + (p.hueOffset % 40) + Math.sin(tt2 * 0.25 + p.phase) * 8) % 360;
382
-
383
337
  // outer soft glow
384
338
  ctx.beginPath();
385
339
  ctx.fillStyle = `hsla(${hueP}, 70%, 75%, ${0.06 + pulse * 0.08})`;
386
340
  ctx.arc(p.x, p.y, p.r * 3.0, 0, Math.PI * 2);
387
341
  ctx.fill();
388
-
389
342
  // bright cool core
390
343
  ctx.beginPath();
391
344
  ctx.fillStyle = `hsla(${hueP}, 65%, 86%, ${0.11 + pulse * 0.14})`;
392
345
  ctx.arc(p.x, p.y, Math.max(1.0, p.r * 1.1), 0, Math.PI * 2);
393
346
  ctx.fill();
394
347
  }
395
-
396
348
  // very light vignette so white background still has depth
397
349
  const vg = ctx.createRadialGradient(
398
350
  w2 / 2,
@@ -406,19 +358,17 @@ const Polyhedron = {
406
358
  vg.addColorStop(1, 'rgba(30,40,60,0.16)');
407
359
  ctx.fillStyle = vg;
408
360
  ctx.fillRect(0, 0, w2, h2);
409
-
410
361
  const drift = 8;
411
- this.Tokens[id].cr[1] += 0.34;
412
- this.Tokens[id].cr[0] += 0.11;
413
- this.Tokens[id].ct[0] = Math.sin(tt2 * 0.6) * drift;
414
- this.Tokens[id].ct[1] = Math.cos(tt2 * 0.45) * drift;
415
- this.Tokens[id].ct[2] = Math.sin(tt2 * 0.38) * (drift * 0.6);
362
+ Polyhedron.Tokens[id].cr[1] += 0.34;
363
+ Polyhedron.Tokens[id].cr[0] += 0.11;
364
+ Polyhedron.Tokens[id].ct[0] = Math.sin(tt2 * 0.6) * drift;
365
+ Polyhedron.Tokens[id].ct[1] = Math.cos(tt2 * 0.45) * drift;
366
+ Polyhedron.Tokens[id].ct[2] = Math.sin(tt2 * 0.38) * (drift * 0.6);
416
367
  } else if (eff === 'noirEmbers') {
417
368
  // FULL FIRE (replaces Noir Embers): bright, high-intensity flame field + hot cores + tongues of fire.
418
369
  // Goal: unmistakably "on fire" and eye-catching, not a subtle ember drift.
419
370
  ctx.fillStyle = 'rgba(0,0,0,1)';
420
371
  ctx.fillRect(0, 0, w2, h2);
421
-
422
372
  // Base flame bed (glowing furnace at the bottom)
423
373
  const bed = ctx.createRadialGradient(w2 * 0.5, h2 * 1.05, 0, w2 * 0.5, h2 * 1.05, Math.min(w2, h2) * 1.05);
424
374
  bed.addColorStop(0, 'hsla(38, 100%, 55%, 0.85)');
@@ -429,7 +379,6 @@ const Polyhedron = {
429
379
  ctx.fillStyle = bed;
430
380
  ctx.fillRect(0, 0, w2, h2);
431
381
  ctx.globalAlpha = 1;
432
-
433
382
  // Heat haze / smoke-lace near the top to add depth (still subtle, fire stays dominant)
434
383
  const haze = ctx.createLinearGradient(0, 0, 0, h2);
435
384
  haze.addColorStop(0, 'rgba(0,0,0,0.55)');
@@ -439,9 +388,8 @@ const Polyhedron = {
439
388
  ctx.fillStyle = haze;
440
389
  ctx.fillRect(0, 0, w2, h2);
441
390
  ctx.globalAlpha = 1;
442
-
443
391
  // Flame tongues + sparks
444
- const ps = this.Tokens[id].immersiveParticles || [];
392
+ const ps = Polyhedron.Tokens[id].immersiveParticles || [];
445
393
  for (const p of ps) {
446
394
  // life cycles: respawn near bottom with new heat/curl to keep it lively
447
395
  p.life += 0.012 + p.flicker * 0.01;
@@ -456,43 +404,35 @@ const Polyhedron = {
456
404
  p.life = 0;
457
405
  p.stretch = 0.6 + (random(0, 1000) / 1000) * 1.6;
458
406
  }
459
-
460
407
  // Rising motion + turbulence (tongues curl side to side)
461
408
  const curl = Math.sin(tt * (1.8 + p.curl * 1.6) + p.phase) * (0.12 + p.curl * 0.18);
462
409
  const wag = Math.sin(tt * 6.5 + p.phase + p.flicker * 10) * 0.06;
463
410
  p.x += (p.vx + curl + wag) * 60;
464
411
  p.y -= (0.25 + Math.abs(p.vy)) * 58;
465
-
466
412
  if (p.x < -60) p.x = w2 + 60;
467
413
  if (p.x > w2 + 60) p.x = -60;
468
-
469
414
  // Intensity ramps up then tapers (flame tongue shape)
470
415
  const ramp = Math.sin(Math.min(1, p.life) * Math.PI); // 0..1..0
471
416
  const flick = 0.45 + 0.55 * Math.max(0, Math.sin(tt * (8 + p.flicker * 7) + p.phase));
472
417
  const heat = 0.25 + 0.75 * p.heat;
473
-
474
418
  // Color: deep red -> orange -> yellow-white hot core
475
419
  const hueP = 10 + heat * 35; // 10..45
476
420
  const lumCore = 70 + heat * 20; // 70..90
477
421
  const lumGlow = 45 + heat * 20; // 45..65
478
-
479
422
  // Tongue body (stretched vertical glow)
480
423
  const tongueH = (14 + heat * 34) * p.stretch * (0.5 + ramp);
481
424
  const tongueW = (3 + heat * 4) * (0.7 + ramp);
482
-
483
425
  ctx.globalAlpha = 0.06 + ramp * 0.16;
484
426
  ctx.fillStyle = `hsla(${hueP}, 100%, ${lumGlow}%, 1)`;
485
427
  ctx.beginPath();
486
428
  ctx.ellipse(p.x, p.y, tongueW * 2.2, tongueH * 1.05, 0, 0, Math.PI * 2);
487
429
  ctx.fill();
488
430
  ctx.globalAlpha = 1;
489
-
490
431
  // Hot core
491
432
  ctx.beginPath();
492
433
  ctx.fillStyle = `hsla(${hueP + 8}, 100%, ${lumCore}%, ${0.1 + ramp * 0.22 * flick})`;
493
434
  ctx.arc(p.x, p.y, Math.max(1.2, p.r * (1.2 + heat * 0.9)), 0, Math.PI * 2);
494
435
  ctx.fill();
495
-
496
436
  // White-hot sparkles (a few particles become bright sparks)
497
437
  const sparkChance = p.i % 9 === 0 ? 1 : 0;
498
438
  if (sparkChance) {
@@ -500,7 +440,6 @@ const Polyhedron = {
500
440
  ctx.fillStyle = `hsla(55, 100%, 92%, ${0.08 + ramp * 0.22 * flick})`;
501
441
  ctx.arc(p.x + curl * 80, p.y - ramp * 18, Math.max(0.8, p.r * 0.75), 0, Math.PI * 2);
502
442
  ctx.fill();
503
-
504
443
  // tiny upward streak
505
444
  ctx.globalAlpha = 0.06 + ramp * 0.12;
506
445
  ctx.strokeStyle = `hsla(48, 100%, 85%, 1)`;
@@ -512,7 +451,6 @@ const Polyhedron = {
512
451
  ctx.globalAlpha = 1;
513
452
  }
514
453
  }
515
-
516
454
  // Stronger vignette to keep fire contrast & "cinematic" intensity
517
455
  const vg = ctx.createRadialGradient(
518
456
  w2 / 2,
@@ -526,52 +464,44 @@ const Polyhedron = {
526
464
  vg.addColorStop(1, 'rgba(0,0,0,0.78)');
527
465
  ctx.fillStyle = vg;
528
466
  ctx.fillRect(0, 0, w2, h2);
529
-
530
467
  // Slightly more energetic polyhedron motion to match the intensity
531
468
  const drift = 9;
532
- this.Tokens[id].cr[1] += 0.46;
533
- this.Tokens[id].cr[0] += 0.15;
534
- this.Tokens[id].cr[2] += 0.08;
535
- this.Tokens[id].ct[0] = Math.sin(tt * 0.7) * drift;
536
- this.Tokens[id].ct[1] = Math.cos(tt * 0.5) * drift;
537
- this.Tokens[id].ct[2] = Math.sin(tt * 0.42) * (drift * 0.65);
469
+ Polyhedron.Tokens[id].cr[1] += 0.46;
470
+ Polyhedron.Tokens[id].cr[0] += 0.15;
471
+ Polyhedron.Tokens[id].cr[2] += 0.08;
472
+ Polyhedron.Tokens[id].ct[0] = Math.sin(tt * 0.7) * drift;
473
+ Polyhedron.Tokens[id].ct[1] = Math.cos(tt * 0.5) * drift;
474
+ Polyhedron.Tokens[id].ct[2] = Math.sin(tt * 0.42) * (drift * 0.65);
538
475
  } else {
539
476
  // Wave light: animated gradient + soft floaty particles (brighter particles)
540
- const hueA = (tt * 18 + this.Tokens[id].immersiveSeed) % 360;
477
+ const hueA = (tt * 18 + Polyhedron.Tokens[id].immersiveSeed) % 360;
541
478
  const hueB = (hueA + 90) % 360;
542
-
543
479
  const g = ctx.createLinearGradient(0, 0, w2, h2);
544
480
  g.addColorStop(0, `hsla(${hueA}, 78%, 12%, 1)`);
545
481
  g.addColorStop(1, `hsla(${hueB}, 78%, 12%, 1)`);
546
482
  ctx.fillStyle = g;
547
483
  ctx.fillRect(0, 0, w2, h2);
548
-
549
- const ps = this.Tokens[id].immersiveParticles || [];
484
+ const ps = Polyhedron.Tokens[id].immersiveParticles || [];
550
485
  for (const p of ps) {
551
486
  p.x += p.vx * 60;
552
487
  p.y += p.vy * 60;
553
-
554
488
  if (p.x < -10) p.x = w2 + 10;
555
489
  if (p.x > w2 + 10) p.x = -10;
556
490
  if (p.y < -10) p.y = h2 + 10;
557
491
  if (p.y > h2 + 10) p.y = -10;
558
-
559
492
  const alpha = 0.22 + 0.16 * Math.sin(tt * 1.7 + p.phase);
560
493
  const hue = (hueA + p.i * 2) % 360;
561
-
562
494
  // glow
563
495
  ctx.beginPath();
564
496
  ctx.fillStyle = `hsla(${hue}, 95%, 78%, ${alpha * 0.45})`;
565
497
  ctx.arc(p.x, p.y, p.r * 2.4, 0, Math.PI * 2);
566
498
  ctx.fill();
567
-
568
499
  // core
569
500
  ctx.beginPath();
570
501
  ctx.fillStyle = `hsla(${hue}, 90%, 72%, ${alpha})`;
571
502
  ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
572
503
  ctx.fill();
573
504
  }
574
-
575
505
  const vg = ctx.createRadialGradient(
576
506
  w2 / 2,
577
507
  h2 / 2,
@@ -584,51 +514,41 @@ const Polyhedron = {
584
514
  vg.addColorStop(1, 'rgba(0,0,0,0.55)');
585
515
  ctx.fillStyle = vg;
586
516
  ctx.fillRect(0, 0, w2, h2);
587
-
588
517
  const drift = 8;
589
- this.Tokens[id].cr[1] += 0.35;
590
- this.Tokens[id].cr[0] += 0.12;
591
- this.Tokens[id].ct[0] = Math.sin(tt * 0.6) * drift;
592
- this.Tokens[id].ct[1] = Math.cos(tt * 0.45) * drift;
593
- this.Tokens[id].ct[2] = Math.sin(tt * 0.35) * (drift * 0.6);
518
+ Polyhedron.Tokens[id].cr[1] += 0.35;
519
+ Polyhedron.Tokens[id].cr[0] += 0.12;
520
+ Polyhedron.Tokens[id].ct[0] = Math.sin(tt * 0.6) * drift;
521
+ Polyhedron.Tokens[id].ct[1] = Math.cos(tt * 0.45) * drift;
522
+ Polyhedron.Tokens[id].ct[2] = Math.sin(tt * 0.35) * (drift * 0.6);
594
523
  }
595
-
596
524
  // Keep face backgrounds synced to current face pixel sizing
597
525
  // (important when immersive resize happens).
598
526
  applyAllFaceBackgrounds();
599
-
600
527
  // Apply transformation now (outside the 200ms interval) for smooth motion.
601
528
  if (s(`.polyhedron-${id}`)) renderTransform();
602
-
603
- this.Tokens[id].immersiveRAF = requestAnimationFrame(tick);
529
+ Polyhedron.Tokens[id].immersiveRAF = requestAnimationFrame(tick);
604
530
  };
605
-
606
- if (this.Tokens[id].immersiveRAF) cancelAnimationFrame(this.Tokens[id].immersiveRAF);
607
- this.Tokens[id].immersiveRAF = requestAnimationFrame(tick);
608
-
531
+ if (Polyhedron.Tokens[id].immersiveRAF) cancelAnimationFrame(Polyhedron.Tokens[id].immersiveRAF);
532
+ Polyhedron.Tokens[id].immersiveRAF = requestAnimationFrame(tick);
609
533
  // Resize handler while immersive
610
534
  const onResize = () => {
611
- if (!this.Tokens[id].immersive) return;
535
+ if (!Polyhedron.Tokens[id].immersive) return;
612
536
  resize();
613
537
  };
614
538
  window.removeEventListener('resize', onResize);
615
539
  window.addEventListener('resize', onResize);
616
- this.Tokens[id].immersiveOnResize = onResize;
617
-
540
+ Polyhedron.Tokens[id].immersiveOnResize = onResize;
618
541
  // Expose reset so the effect toggle can rebuild particles quickly.
619
- this.Tokens[id]._resetImmersiveParticles = resetParticlesForEffect;
542
+ Polyhedron.Tokens[id]._resetImmersiveParticles = resetParticlesForEffect;
620
543
  };
621
-
622
544
  const stopImmersiveEffects = () => {
623
- if (this.Tokens[id].immersiveRAF) cancelAnimationFrame(this.Tokens[id].immersiveRAF);
624
- this.Tokens[id].immersiveRAF = null;
625
- this.Tokens[id].immersiveParticles = null;
626
-
627
- if (this.Tokens[id].immersiveOnResize) {
628
- window.removeEventListener('resize', this.Tokens[id].immersiveOnResize);
629
- this.Tokens[id].immersiveOnResize = null;
545
+ if (Polyhedron.Tokens[id].immersiveRAF) cancelAnimationFrame(Polyhedron.Tokens[id].immersiveRAF);
546
+ Polyhedron.Tokens[id].immersiveRAF = null;
547
+ Polyhedron.Tokens[id].immersiveParticles = null;
548
+ if (Polyhedron.Tokens[id].immersiveOnResize) {
549
+ window.removeEventListener('resize', Polyhedron.Tokens[id].immersiveOnResize);
550
+ Polyhedron.Tokens[id].immersiveOnResize = null;
630
551
  }
631
-
632
552
  // Clear + hide canvas so the last rendered frame is not left visible.
633
553
  const canvas = s(`.polyhedron-immersive-canvas-${id}`);
634
554
  if (canvas) {
@@ -636,17 +556,14 @@ const Polyhedron = {
636
556
  if (ctx) ctx.clearRect(0, 0, canvas.width || 0, canvas.height || 0);
637
557
  canvas.style.display = 'none';
638
558
  }
639
-
640
559
  // Reset drift so manual controls feel normal again
641
- this.Tokens[id].ct = [0, 0, 0];
560
+ Polyhedron.Tokens[id].ct = [0, 0, 0];
642
561
  };
643
-
644
562
  const setImmersive = (isImmersive) => {
645
- this.Tokens[id].immersive = !!isImmersive;
563
+ Polyhedron.Tokens[id].immersive = !!isImmersive;
646
564
  const scene = s(`.scene-${id}`);
647
565
  if (!scene) return;
648
-
649
- if (this.Tokens[id].immersive) {
566
+ if (Polyhedron.Tokens[id].immersive) {
650
567
  scene.classList.add(`scene-immersive-${id}`);
651
568
  startImmersiveEffects();
652
569
  } else {
@@ -654,149 +571,129 @@ const Polyhedron = {
654
571
  stopImmersiveEffects();
655
572
  }
656
573
  };
657
-
658
574
  const renderTransform = () => {
659
575
  s(`.polyhedron-${id}`).style.transform =
660
- `rotateX(${this.Tokens[id].cr[0]}deg) rotateY(${this.Tokens[id].cr[1]}deg) rotateZ(${this.Tokens[id].cr[2]}deg)
661
- translateX(${this.Tokens[id].ct[0]}px) translateY(${this.Tokens[id].ct[1]}px) translateZ(${this.Tokens[id].ct[2]}px)`;
662
- s(`.polyhedron-${id}`).style.left = `${s(`.scene-${id}`).offsetWidth / 2 - this.Tokens[id].dim / 2}px`;
663
- s(`.polyhedron-${id}`).style.top = `${s(`.scene-${id}`).offsetHeight / 2 - this.Tokens[id].dim / 2}px`;
664
- s(`.polyhedron-${id}`).style.width = `${this.Tokens[id].dim}px`;
665
- s(`.polyhedron-${id}`).style.height = `${this.Tokens[id].dim}px`;
576
+ `rotateX(${Polyhedron.Tokens[id].cr[0]}deg) rotateY(${Polyhedron.Tokens[id].cr[1]}deg) rotateZ(${Polyhedron.Tokens[id].cr[2]}deg)
577
+ translateX(${Polyhedron.Tokens[id].ct[0]}px) translateY(${Polyhedron.Tokens[id].ct[1]}px) translateZ(${Polyhedron.Tokens[id].ct[2]}px)`;
578
+ s(`.polyhedron-${id}`).style.left = `${s(`.scene-${id}`).offsetWidth / 2 - Polyhedron.Tokens[id].dim / 2}px`;
579
+ s(`.polyhedron-${id}`).style.top = `${s(`.scene-${id}`).offsetHeight / 2 - Polyhedron.Tokens[id].dim / 2}px`;
580
+ s(`.polyhedron-${id}`).style.width = `${Polyhedron.Tokens[id].dim}px`;
581
+ s(`.polyhedron-${id}`).style.height = `${Polyhedron.Tokens[id].dim}px`;
666
582
  /* rotate Y */
667
- s(`.face_front-${id}`).style.transform = `rotateY(0deg) translateZ(${this.Tokens[id].dim / 2}px)`;
668
- s(`.face_back-${id}`).style.transform = `rotateY(-180deg) translateZ(${this.Tokens[id].dim / 2}px)`;
669
- s(`.face_left-${id}`).style.transform = `rotateY(90deg) translateZ(${this.Tokens[id].dim / 2}px)`;
670
- s(`.face_right-${id}`).style.transform = `rotateY(-90deg) translateZ(${this.Tokens[id].dim / 2}px)`;
583
+ s(`.face_front-${id}`).style.transform = `rotateY(0deg) translateZ(${Polyhedron.Tokens[id].dim / 2}px)`;
584
+ s(`.face_back-${id}`).style.transform = `rotateY(-180deg) translateZ(${Polyhedron.Tokens[id].dim / 2}px)`;
585
+ s(`.face_left-${id}`).style.transform = `rotateY(90deg) translateZ(${Polyhedron.Tokens[id].dim / 2}px)`;
586
+ s(`.face_right-${id}`).style.transform = `rotateY(-90deg) translateZ(${Polyhedron.Tokens[id].dim / 2}px)`;
671
587
  /* rotate X */
672
- s(`.face_top-${id}`).style.transform = `rotateX(-90deg) translateZ(${this.Tokens[id].dim / 2}px)`;
673
- s(`.face_bottom-${id}`).style.transform = `rotateX(90deg) translateZ(${this.Tokens[id].dim / 2}px)`;
588
+ s(`.face_top-${id}`).style.transform = `rotateX(-90deg) translateZ(${Polyhedron.Tokens[id].dim / 2}px)`;
589
+ s(`.face_bottom-${id}`).style.transform = `rotateX(90deg) translateZ(${Polyhedron.Tokens[id].dim / 2}px)`;
674
590
  };
675
- if (this.Tokens[id].interval) clearInterval(this.Tokens[id].interval);
676
- this.Tokens[id].interval = setInterval(() => {
591
+ if (Polyhedron.Tokens[id].interval) clearInterval(Polyhedron.Tokens[id].interval);
592
+ Polyhedron.Tokens[id].interval = setInterval(() => {
677
593
  if (s(`.polyhedron-${id}`)) renderTransform();
678
- else return clearInterval(this.Tokens[id].interval);
594
+ else return clearInterval(Polyhedron.Tokens[id].interval);
679
595
  }, 200);
680
-
681
596
  setTimeout(() => {
682
597
  renderTransform();
683
598
  s(`.polyhedron-${id}`).style.transition = `.4s`;
684
599
  applyAllFaceBackgrounds();
685
600
  applyFaceOpacity();
686
- setImmersive(this.Tokens[id].immersive);
687
-
601
+ setImmersive(Polyhedron.Tokens[id].immersive);
688
602
  s(`.btn-polyhedron-rotate-down-${id}`).onclick = () => {
689
- this.Tokens[id].cr[0] += 45;
603
+ Polyhedron.Tokens[id].cr[0] += 45;
690
604
  };
691
605
  s(`.btn-polyhedron-rotate-up-${id}`).onclick = () => {
692
- this.Tokens[id].cr[0] -= 45;
606
+ Polyhedron.Tokens[id].cr[0] -= 45;
693
607
  };
694
608
  s(`.btn-polyhedron-rotate-left-${id}`).onclick = () => {
695
- this.Tokens[id].cr[1] += 45;
609
+ Polyhedron.Tokens[id].cr[1] += 45;
696
610
  };
697
611
  s(`.btn-polyhedron-rotate-right-${id}`).onclick = () => {
698
- this.Tokens[id].cr[1] -= 45;
612
+ Polyhedron.Tokens[id].cr[1] -= 45;
699
613
  };
700
-
701
614
  s(`.btn-polyhedron-add-zoom-${id}`).onclick = () => {
702
- this.Tokens[id].dim += 25;
615
+ Polyhedron.Tokens[id].dim += 25;
703
616
  };
704
617
  s(`.btn-polyhedron-remove-zoom-${id}`).onclick = () => {
705
- this.Tokens[id].dim -= 25;
618
+ Polyhedron.Tokens[id].dim -= 25;
706
619
  };
707
-
708
620
  const facePicker = s(`.polyhedron-face-picker-${id}`);
709
621
  if (facePicker) {
710
622
  // Keep UI and state in sync on re-render
711
- facePicker.value = this.Tokens[id].chosenFace || 'front';
712
-
623
+ facePicker.value = Polyhedron.Tokens[id].chosenFace || 'front';
713
624
  facePicker.onchange = (e) => {
714
- this.Tokens[id].chosenFace = e?.target?.value || 'front';
625
+ Polyhedron.Tokens[id].chosenFace = e?.target?.value || 'front';
715
626
  };
716
627
  }
717
-
718
628
  const opacitySlider = s(`.polyhedron-face-opacity-${id}`);
719
629
  if (opacitySlider) {
720
630
  // Keep UI and state in sync on re-render
721
- opacitySlider.value = `${this.Tokens[id].faceOpacity}`;
722
-
631
+ opacitySlider.value = `${Polyhedron.Tokens[id].faceOpacity}`;
723
632
  opacitySlider.oninput = (e) => {
724
633
  const v = parseFloat(e?.target?.value);
725
- this.Tokens[id].faceOpacity = Number.isFinite(v) ? Math.max(0, Math.min(1, v)) : 1;
634
+ Polyhedron.Tokens[id].faceOpacity = Number.isFinite(v) ? Math.max(0, Math.min(1, v)) : 1;
726
635
  applyFaceOpacity();
727
636
  };
728
637
  }
729
-
730
638
  const fileInput = s(`.polyhedron-face-image-file-${id}`);
731
639
  if (fileInput) {
732
640
  fileInput.onchange = (e) => {
733
641
  const file = e?.target?.files?.[0];
734
642
  if (!file) return;
735
-
736
643
  const url = URL.createObjectURL(file);
737
- const face = this.Tokens[id].chosenFace || 'front';
738
-
644
+ const face = Polyhedron.Tokens[id].chosenFace || 'front';
739
645
  if (face === 'all') {
740
646
  ['front', 'back', 'left', 'right', 'top', 'bottom'].forEach((f) => {
741
- this.Tokens[id].faces[f] = url;
647
+ Polyhedron.Tokens[id].faces[f] = url;
742
648
  });
743
649
  applyAllFaceBackgrounds();
744
650
  } else {
745
- this.Tokens[id].faces[face] = url;
651
+ Polyhedron.Tokens[id].faces[face] = url;
746
652
  applyFaceBackground(face);
747
653
  }
748
-
749
654
  fileInput.value = '';
750
655
  };
751
656
  }
752
-
753
657
  const clearFaceBtn = s(`.btn-polyhedron-clear-face-image-${id}`);
754
658
  if (clearFaceBtn) {
755
659
  clearFaceBtn.onclick = () => {
756
- const face = this.Tokens[id].chosenFace || 'front';
757
-
660
+ const face = Polyhedron.Tokens[id].chosenFace || 'front';
758
661
  if (face === 'all') {
759
662
  ['front', 'back', 'left', 'right', 'top', 'bottom'].forEach((f) => {
760
- this.Tokens[id].faces[f] = null;
663
+ Polyhedron.Tokens[id].faces[f] = null;
761
664
  });
762
665
  applyAllFaceBackgrounds();
763
666
  } else {
764
- this.Tokens[id].faces[face] = null;
667
+ Polyhedron.Tokens[id].faces[face] = null;
765
668
  applyFaceBackground(face);
766
669
  }
767
670
  };
768
671
  }
769
-
770
672
  const immersiveBtn = s(`.btn-polyhedron-immersive-${id}`);
771
673
  if (immersiveBtn) {
772
- immersiveBtn.onclick = () => setImmersive(!this.Tokens[id].immersive);
674
+ immersiveBtn.onclick = () => setImmersive(!Polyhedron.Tokens[id].immersive);
773
675
  }
774
-
775
676
  const immersiveExitBtn = s(`.btn-polyhedron-immersive-exit-${id}`);
776
677
  if (immersiveExitBtn) {
777
678
  immersiveExitBtn.onclick = () => setImmersive(false);
778
679
  }
779
-
780
680
  const immersiveEffectBtn = s(`.btn-polyhedron-immersive-effect-${id}`);
781
681
  if (immersiveEffectBtn) {
782
682
  immersiveEffectBtn.onclick = () => {
783
683
  const order = ['waveLight', 'prismBloom', 'darkElectronic', 'noirEmbers'];
784
- const curr = this.Tokens[id].immersiveEffect || 'waveLight';
684
+ const curr = Polyhedron.Tokens[id].immersiveEffect || 'waveLight';
785
685
  const idx = order.indexOf(curr);
786
- this.Tokens[id].immersiveEffect = order[(idx + 1) % order.length] || 'waveLight';
787
-
788
- if (this.Tokens[id].immersive && this.Tokens[id]._resetImmersiveParticles)
789
- this.Tokens[id]._resetImmersiveParticles();
686
+ Polyhedron.Tokens[id].immersiveEffect = order[(idx + 1) % order.length] || 'waveLight';
687
+ if (Polyhedron.Tokens[id].immersive && Polyhedron.Tokens[id]._resetImmersiveParticles)
688
+ Polyhedron.Tokens[id]._resetImmersiveParticles();
790
689
  };
791
690
  }
792
-
793
691
  const escHandler = (ev) => {
794
- if (ev?.key === 'Escape' && this.Tokens[id].immersive) setImmersive(false);
692
+ if (ev?.key === 'Escape' && Polyhedron.Tokens[id].immersive) setImmersive(false);
795
693
  };
796
694
  document.removeEventListener('keydown', escHandler);
797
695
  document.addEventListener('keydown', escHandler);
798
696
  });
799
-
800
697
  return html`
801
698
  <style>
802
699
  .scene-${id} {
@@ -956,29 +853,29 @@ const Polyhedron = {
956
853
  <div class="in fll polyhedron-${id}-col-a">
957
854
  <div class="in section-mp">
958
855
  <div class="in sub-title-modal"><i class="fa-solid fa-arrows-spin"></i> Rotate</div>
959
- ${await BtnIcon.Render({
856
+ ${await BtnIcon.instance({
960
857
  class: `inl section-mp btn-custom btn-polyhedron-rotate-up-${id}`,
961
858
  label: html`<i class="fa-solid fa-angle-up"></i>`,
962
859
  })}
963
- ${await BtnIcon.Render({
860
+ ${await BtnIcon.instance({
964
861
  class: `inl section-mp btn-custom btn-polyhedron-rotate-down-${id}`,
965
862
  label: html`<i class="fa-solid fa-angle-down"></i>`,
966
863
  })}
967
- ${await BtnIcon.Render({
864
+ ${await BtnIcon.instance({
968
865
  class: `inl section-mp btn-custom btn-polyhedron-rotate-left-${id}`,
969
866
  label: html`<i class="fa-solid fa-angle-left"></i>`,
970
867
  })}
971
- ${await BtnIcon.Render({
868
+ ${await BtnIcon.instance({
972
869
  class: `inl section-mp btn-custom btn-polyhedron-rotate-right-${id}`,
973
870
  label: html`<i class="fa-solid fa-angle-right"></i>`,
974
871
  })}
975
872
 
976
873
  <div class="in sub-title-modal"><i class="fa-solid fa-magnifying-glass"></i> Zoom</div>
977
- ${await BtnIcon.Render({
874
+ ${await BtnIcon.instance({
978
875
  class: `inl section-mp btn-custom btn-polyhedron-add-zoom-${id}`,
979
876
  label: html`<i class="fa-solid fa-plus"></i>`,
980
877
  })}
981
- ${await BtnIcon.Render({
878
+ ${await BtnIcon.instance({
982
879
  class: `inl section-mp btn-custom btn-polyhedron-remove-zoom-${id}`,
983
880
  label: html`<i class="fa-solid fa-minus"></i>`,
984
881
  })}
@@ -990,7 +887,7 @@ const Polyhedron = {
990
887
  min="0"
991
888
  max="1"
992
889
  step="0.01"
993
- value="${this.Tokens[id].faceOpacity}"
890
+ value="${Polyhedron.Tokens[id].faceOpacity}"
994
891
  title="Face opacity"
995
892
  />
996
893
 
@@ -1006,11 +903,11 @@ const Polyhedron = {
1006
903
  <option value="bottom">bottom</option>
1007
904
  </select>
1008
905
  <input class="in polyhedron-face-image-file-${id}" type="file" accept="image/*" />
1009
- ${await BtnIcon.Render({
906
+ ${await BtnIcon.instance({
1010
907
  class: `inl section-mp btn-custom btn-polyhedron-clear-face-image-${id}`,
1011
- label: html`<i class="fa-solid fa-eraser"></i> ${Translate.Render('clear')}`,
908
+ label: html`<i class="fa-solid fa-eraser"></i> ${Translate.instance('clear')}`,
1012
909
  })}
1013
- ${await BtnIcon.Render({
910
+ ${await BtnIcon.instance({
1014
911
  class: `inl section-mp btn-custom btn-polyhedron-immersive-${id}`,
1015
912
  label: html`<i class="fa-solid fa-expand"></i> Immersive`,
1016
913
  })}
@@ -1018,7 +915,7 @@ const Polyhedron = {
1018
915
  </div>
1019
916
  <div class="in fll polyhedron-${id}-col-b">
1020
917
  <div class="in section-mp">
1021
- <div class="in sub-title-modal"><i class="fa-solid fa-vector-square"></i> Render</div>
918
+ <div class="in sub-title-modal"><i class="fa-solid fa-vector-square"></i> instance</div>
1022
919
  <div class="in scene-${id}">
1023
920
  <canvas class="polyhedron-immersive-canvas-${id}"></canvas>
1024
921
 
@@ -1045,7 +942,6 @@ const Polyhedron = {
1045
942
  </div>
1046
943
  </div>
1047
944
  `;
1048
- },
1049
- };
1050
-
945
+ }
946
+ }
1051
947
  export { Polyhedron };