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.
- package/.github/workflows/release.cd.yml +1 -2
- package/CHANGELOG.md +268 -1
- package/CLI-HELP.md +26 -13
- package/Dockerfile +0 -4
- package/README.md +3 -3
- package/bin/build.js +13 -3
- package/bin/deploy.js +570 -1
- package/bin/file.js +5 -0
- package/conf.js +11 -2
- package/jsconfig.json +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -3
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +2 -3
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -6
- package/manifests/deployment/dd-test-development/deployment.yaml +136 -66
- package/manifests/deployment/dd-test-development/proxy.yaml +41 -5
- package/package.json +20 -11
- package/src/api/core/core.controller.js +10 -10
- package/src/api/core/core.service.js +10 -10
- package/src/api/default/default.controller.js +10 -10
- package/src/api/default/default.service.js +10 -10
- package/src/api/document/document.controller.js +12 -12
- package/src/api/document/document.model.js +10 -16
- package/src/api/file/file.controller.js +8 -8
- package/src/api/file/file.model.js +10 -10
- package/src/api/file/file.service.js +36 -36
- package/src/api/test/test.controller.js +8 -8
- package/src/api/test/test.service.js +8 -8
- package/src/api/user/guest.service.js +99 -0
- package/src/api/user/user.controller.js +6 -6
- package/src/api/user/user.model.js +8 -13
- package/src/api/user/user.service.js +3 -20
- package/src/cli/deploy.js +33 -30
- package/src/cli/fs.js +62 -5
- package/src/cli/image.js +43 -1
- package/src/cli/index.js +5 -1
- package/src/cli/release.js +58 -2
- package/src/cli/repository.js +35 -3
- package/src/cli/run.js +304 -38
- package/src/cli/ssh.js +1 -1
- package/src/cli/static.js +43 -115
- package/src/client/Default.index.js +21 -33
- package/src/client/components/core/404.js +4 -4
- package/src/client/components/core/500.js +4 -4
- package/src/client/components/core/Account.js +73 -60
- package/src/client/components/core/AgGrid.js +23 -33
- package/src/client/components/core/Alert.js +12 -13
- package/src/client/components/core/AppStore.js +1 -1
- package/src/client/components/core/Auth.js +20 -32
- package/src/client/components/core/Badge.js +7 -13
- package/src/client/components/core/BtnIcon.js +15 -17
- package/src/client/components/core/CalendarCore.js +42 -63
- package/src/client/components/core/Chat.js +13 -15
- package/src/client/components/core/ClientEvents.js +87 -0
- package/src/client/components/core/ColorPaletteElement.js +309 -0
- package/src/client/components/core/Content.js +17 -14
- package/src/client/components/core/Css.js +15 -71
- package/src/client/components/core/CssCore.js +12 -16
- package/src/client/components/core/D3Chart.js +4 -4
- package/src/client/components/core/Docs.js +60 -59
- package/src/client/components/core/DropDown.js +69 -91
- package/src/client/components/core/EventBus.js +92 -0
- package/src/client/components/core/EventsUI.js +14 -17
- package/src/client/components/core/FileExplorer.js +102 -234
- package/src/client/components/core/FullScreen.js +47 -75
- package/src/client/components/core/Input.js +24 -69
- package/src/client/components/core/Keyboard.js +25 -18
- package/src/client/components/core/KeyboardAvoidance.js +145 -0
- package/src/client/components/core/LoadingAnimation.js +25 -31
- package/src/client/components/core/LogIn.js +41 -41
- package/src/client/components/core/LogOut.js +23 -14
- package/src/client/components/core/Modal.js +397 -176
- package/src/client/components/core/NotificationManager.js +14 -18
- package/src/client/components/core/Panel.js +54 -50
- package/src/client/components/core/PanelForm.js +25 -125
- package/src/client/components/core/Polyhedron.js +110 -214
- package/src/client/components/core/PublicProfile.js +39 -32
- package/src/client/components/core/Recover.js +52 -48
- package/src/client/components/core/Responsive.js +88 -32
- package/src/client/components/core/RichText.js +9 -18
- package/src/client/components/core/Router.js +24 -3
- package/src/client/components/core/SearchBox.js +37 -37
- package/src/client/components/core/SignUp.js +39 -30
- package/src/client/components/core/SocketIo.js +31 -2
- package/src/client/components/core/SocketIoHandler.js +6 -6
- package/src/client/components/core/ToggleSwitch.js +8 -20
- package/src/client/components/core/ToolTip.js +5 -17
- package/src/client/components/core/Translate.js +56 -59
- package/src/client/components/core/Validator.js +26 -16
- package/src/client/components/core/Wallet.js +15 -26
- package/src/client/components/core/Worker.js +140 -25
- package/src/client/components/core/windowGetDimensions.js +7 -7
- package/src/client/components/default/{MenuDefault.js → AppShellDefault.js} +87 -87
- package/src/client/components/default/CssDefault.js +12 -12
- package/src/client/components/default/LogInDefault.js +6 -4
- package/src/client/components/default/LogOutDefault.js +6 -4
- package/src/client/components/default/RouterDefault.js +47 -0
- package/src/client/components/default/SettingsDefault.js +4 -4
- package/src/client/components/default/SignUpDefault.js +6 -4
- package/src/client/components/default/TranslateDefault.js +3 -3
- package/src/client/services/core/core.service.js +17 -49
- package/src/client/services/default/default.management.js +139 -242
- package/src/client/services/default/default.service.js +10 -16
- package/src/client/services/document/document.service.js +14 -19
- package/src/client/services/file/file.service.js +8 -13
- package/src/client/services/test/test.service.js +8 -13
- package/src/client/services/user/guest.service.js +79 -0
- package/src/client/services/user/user.management.js +5 -5
- package/src/client/services/user/user.service.js +14 -20
- package/src/client/ssr/body/404.js +3 -3
- package/src/client/ssr/body/500.js +3 -3
- package/src/client/ssr/body/CacheControl.js +5 -2
- package/src/client/ssr/body/DefaultSplashScreen.js +19 -12
- package/src/client/ssr/mailer/DefaultRecoverEmail.js +19 -20
- package/src/client/ssr/mailer/DefaultVerifyEmail.js +15 -16
- package/src/client/ssr/offline/Maintenance.js +12 -11
- package/src/client/ssr/offline/NoNetworkConnection.js +3 -3
- package/src/client/ssr/pages/Test.js +2 -2
- package/src/client/sw/core.sw.js +212 -0
- package/src/index.js +1 -1
- package/src/runtime/express/Dockerfile +4 -4
- package/src/runtime/lampp/Dockerfile +8 -7
- package/src/runtime/wp/Dockerfile +11 -17
- package/src/server/backup.js +1 -2
- package/src/server/client-build-docs.js +45 -46
- package/src/server/client-build.js +334 -60
- package/src/server/client-formatted.js +47 -16
- package/src/server/conf.js +29 -13
- package/src/server/cron.js +6 -8
- package/src/server/dns.js +2 -1
- package/src/server/ipfs-client.js +232 -91
- package/src/server/process.js +13 -27
- package/src/server/start.js +6 -3
- package/src/server/valkey.js +134 -235
- package/tsconfig.docs.json +15 -0
- package/typedoc.json +20 -0
- package/jsdoc.json +0 -52
- package/src/client/components/core/ColorPalette.js +0 -5267
- package/src/client/components/core/JoyStick.js +0 -80
- package/src/client/components/default/RoutesDefault.js +0 -49
- package/src/client/sw/default.sw.js +0 -127
- 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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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 =
|
|
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
|
|
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 =
|
|
173
|
+
const eff = Polyhedron.Tokens[id].immersiveEffect || 'waveLight';
|
|
191
174
|
console.error(eff);
|
|
192
|
-
if (eff === 'darkElectronic')
|
|
193
|
-
else if (eff === 'prismBloom')
|
|
194
|
-
else if (eff === 'noirEmbers')
|
|
195
|
-
else
|
|
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
|
-
|
|
200
|
-
|
|
181
|
+
Polyhedron.Tokens[id].immersiveStart = performance.now();
|
|
201
182
|
const tick = (t) => {
|
|
202
|
-
if (!
|
|
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
|
|
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 =
|
|
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
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
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 =
|
|
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
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
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 +
|
|
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
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
617
|
-
|
|
540
|
+
Polyhedron.Tokens[id].immersiveOnResize = onResize;
|
|
618
541
|
// Expose reset so the effect toggle can rebuild particles quickly.
|
|
619
|
-
|
|
542
|
+
Polyhedron.Tokens[id]._resetImmersiveParticles = resetParticlesForEffect;
|
|
620
543
|
};
|
|
621
|
-
|
|
622
544
|
const stopImmersiveEffects = () => {
|
|
623
|
-
if (
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
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
|
-
|
|
560
|
+
Polyhedron.Tokens[id].ct = [0, 0, 0];
|
|
642
561
|
};
|
|
643
|
-
|
|
644
562
|
const setImmersive = (isImmersive) => {
|
|
645
|
-
|
|
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(${
|
|
661
|
-
translateX(${
|
|
662
|
-
s(`.polyhedron-${id}`).style.left = `${s(`.scene-${id}`).offsetWidth / 2 -
|
|
663
|
-
s(`.polyhedron-${id}`).style.top = `${s(`.scene-${id}`).offsetHeight / 2 -
|
|
664
|
-
s(`.polyhedron-${id}`).style.width = `${
|
|
665
|
-
s(`.polyhedron-${id}`).style.height = `${
|
|
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(${
|
|
668
|
-
s(`.face_back-${id}`).style.transform = `rotateY(-180deg) translateZ(${
|
|
669
|
-
s(`.face_left-${id}`).style.transform = `rotateY(90deg) translateZ(${
|
|
670
|
-
s(`.face_right-${id}`).style.transform = `rotateY(-90deg) translateZ(${
|
|
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(${
|
|
673
|
-
s(`.face_bottom-${id}`).style.transform = `rotateX(90deg) translateZ(${
|
|
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 (
|
|
676
|
-
|
|
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(
|
|
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(
|
|
687
|
-
|
|
601
|
+
setImmersive(Polyhedron.Tokens[id].immersive);
|
|
688
602
|
s(`.btn-polyhedron-rotate-down-${id}`).onclick = () => {
|
|
689
|
-
|
|
603
|
+
Polyhedron.Tokens[id].cr[0] += 45;
|
|
690
604
|
};
|
|
691
605
|
s(`.btn-polyhedron-rotate-up-${id}`).onclick = () => {
|
|
692
|
-
|
|
606
|
+
Polyhedron.Tokens[id].cr[0] -= 45;
|
|
693
607
|
};
|
|
694
608
|
s(`.btn-polyhedron-rotate-left-${id}`).onclick = () => {
|
|
695
|
-
|
|
609
|
+
Polyhedron.Tokens[id].cr[1] += 45;
|
|
696
610
|
};
|
|
697
611
|
s(`.btn-polyhedron-rotate-right-${id}`).onclick = () => {
|
|
698
|
-
|
|
612
|
+
Polyhedron.Tokens[id].cr[1] -= 45;
|
|
699
613
|
};
|
|
700
|
-
|
|
701
614
|
s(`.btn-polyhedron-add-zoom-${id}`).onclick = () => {
|
|
702
|
-
|
|
615
|
+
Polyhedron.Tokens[id].dim += 25;
|
|
703
616
|
};
|
|
704
617
|
s(`.btn-polyhedron-remove-zoom-${id}`).onclick = () => {
|
|
705
|
-
|
|
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 =
|
|
712
|
-
|
|
623
|
+
facePicker.value = Polyhedron.Tokens[id].chosenFace || 'front';
|
|
713
624
|
facePicker.onchange = (e) => {
|
|
714
|
-
|
|
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 = `${
|
|
722
|
-
|
|
631
|
+
opacitySlider.value = `${Polyhedron.Tokens[id].faceOpacity}`;
|
|
723
632
|
opacitySlider.oninput = (e) => {
|
|
724
633
|
const v = parseFloat(e?.target?.value);
|
|
725
|
-
|
|
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 =
|
|
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
|
-
|
|
647
|
+
Polyhedron.Tokens[id].faces[f] = url;
|
|
742
648
|
});
|
|
743
649
|
applyAllFaceBackgrounds();
|
|
744
650
|
} else {
|
|
745
|
-
|
|
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 =
|
|
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
|
-
|
|
663
|
+
Polyhedron.Tokens[id].faces[f] = null;
|
|
761
664
|
});
|
|
762
665
|
applyAllFaceBackgrounds();
|
|
763
666
|
} else {
|
|
764
|
-
|
|
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(!
|
|
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 =
|
|
684
|
+
const curr = Polyhedron.Tokens[id].immersiveEffect || 'waveLight';
|
|
785
685
|
const idx = order.indexOf(curr);
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
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' &&
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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="${
|
|
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.
|
|
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.
|
|
908
|
+
label: html`<i class="fa-solid fa-eraser"></i> ${Translate.instance('clear')}`,
|
|
1012
909
|
})}
|
|
1013
|
-
${await BtnIcon.
|
|
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>
|
|
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 };
|