cyberia 3.0.2 → 3.0.3
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/CHANGELOG.md +323 -290
- package/CLI-HELP.md +2 -1
- package/bin/build.js +0 -1
- package/bin/cyberia.js +98 -4
- package/bin/index.js +98 -4
- package/conf.js +192 -0
- package/deployment.yaml +72 -2
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/package.json +13 -9
- package/proxy.yaml +56 -9
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +40 -7
- package/src/api/object-layer/object-layer.model.js +61 -19
- package/src/api/object-layer/object-layer.service.js +4 -9
- package/src/cli/index.js +6 -0
- package/src/cli/run.js +30 -2
- package/src/client/Underpost.index.js +36 -0
- package/src/client/components/core/Modal.js +2 -0
- package/src/client/components/core/PublicProfile.js +3 -3
- package/src/client/components/core/Router.js +34 -1
- package/src/client/components/core/Worker.js +1 -1
- package/src/client/components/cryptokoyn/CssCryptokoyn.js +63 -1
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +145 -119
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +64 -6
- package/src/client/components/itemledger/CssItemledger.js +62 -0
- package/src/client/components/underpost/CommonUnderpost.js +29 -0
- package/src/client/components/underpost/CssUnderpost.js +222 -0
- package/src/client/components/underpost/CyberpunkBloggerUnderpost.js +879 -0
- package/src/client/components/underpost/DocumentSearchProvider.js +448 -0
- package/src/client/components/underpost/ElementsUnderpost.js +38 -0
- package/src/client/components/underpost/LabGalleryUnderpost.js +82 -0
- package/src/client/components/underpost/LogInUnderpost.js +20 -0
- package/src/client/components/underpost/LogOutUnderpost.js +13 -0
- package/src/client/components/underpost/MenuUnderpost.js +605 -0
- package/src/client/components/underpost/RoutesUnderpost.js +45 -0
- package/src/client/components/underpost/SettingsUnderpost.js +16 -0
- package/src/client/components/underpost/SignUpUnderpost.js +9 -0
- package/src/client/components/underpost/SocketIoUnderpost.js +54 -0
- package/src/client/components/underpost/TranslateUnderpost.js +10 -0
- package/src/client/services/object-layer/object-layer.management.js +23 -4
- package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +83 -0
- package/src/client/ssr/head/UnderpostScripts.js +6 -0
- package/src/index.js +1 -1
- package/src/server/object-layer.js +13 -10
- package/src/server/semantic-layer-generator.js +1 -0
package/src/cli/run.js
CHANGED
|
@@ -32,7 +32,6 @@ const logger = loggerFactory(import.meta);
|
|
|
32
32
|
* @property {string} podName - The name of the pod to run.
|
|
33
33
|
* @property {string} nodeName - The name of the node to run.
|
|
34
34
|
* @property {number} port - Custom port to use.
|
|
35
|
-
* @property {boolean} etcHosts - Whether to modify /etc/hosts.
|
|
36
35
|
* @property {string} volumeHostPath - The host path for the volume.
|
|
37
36
|
* @property {string} volumeMountPath - The mount path for the volume.
|
|
38
37
|
* @property {string} imageName - The name of the image to run.
|
|
@@ -85,9 +84,12 @@ const logger = loggerFactory(import.meta);
|
|
|
85
84
|
* @property {string} monitorStatusKindType - The monitor status kind type option.
|
|
86
85
|
* @property {string} monitorStatusDeltaMs - The monitor status delta in milliseconds.
|
|
87
86
|
* @property {string} monitorStatusMaxAttempts - The maximum number of attempts for monitor status.
|
|
87
|
+
* @property {boolean} logs - Whether to enable logs.
|
|
88
88
|
* @property {boolean} dryRun - Whether to perform a dry run.
|
|
89
89
|
* @property {boolean} createJobNow - Whether to create the job immediately.
|
|
90
|
-
* @property {
|
|
90
|
+
* @property {string|Array<{ip: string, hostnames: string[]}>} hostAliases - Adds entries to the Pod /etc/hosts via Kubernetes hostAliases.
|
|
91
|
+
* As a string (CLI): semicolon-separated entries of "ip=hostname1,hostname2" (e.g., "127.0.0.1=foo.local,bar.local;10.1.2.3=foo.remote").
|
|
92
|
+
* As an array (programmatic): objects with `ip` and `hostnames` fields (e.g., [{ ip: "127.0.0.1", hostnames: ["foo.local"] }]).
|
|
91
93
|
* @memberof UnderpostRun
|
|
92
94
|
*/
|
|
93
95
|
const DEFAULT_OPTION = {
|
|
@@ -150,6 +152,7 @@ const DEFAULT_OPTION = {
|
|
|
150
152
|
logs: false,
|
|
151
153
|
dryRun: false,
|
|
152
154
|
createJobNow: false,
|
|
155
|
+
hostAliases: '',
|
|
153
156
|
};
|
|
154
157
|
|
|
155
158
|
/**
|
|
@@ -1840,6 +1843,30 @@ EOF
|
|
|
1840
1843
|
const imagePullPolicy = options.imagePullPolicy || 'IfNotPresent';
|
|
1841
1844
|
const hostNetwork = options.hostNetwork ? options.hostNetwork : '';
|
|
1842
1845
|
const apiVersion = options.apiVersion || 'v1';
|
|
1846
|
+
// Parse hostAliases option:
|
|
1847
|
+
// - string from CLI: "ip1=host1,host2;ip2=host3,host4"
|
|
1848
|
+
// - array from programmatic callers: [{ ip: "127.0.0.1", hostnames: ["foo.local"] }]
|
|
1849
|
+
const hostAliases = options.hostAliases
|
|
1850
|
+
? Array.isArray(options.hostAliases)
|
|
1851
|
+
? options.hostAliases
|
|
1852
|
+
: options.hostAliases
|
|
1853
|
+
.split(';')
|
|
1854
|
+
.filter((entry) => entry.trim())
|
|
1855
|
+
.map((entry) => {
|
|
1856
|
+
const [ip, hostnamesStr] = entry.split('=');
|
|
1857
|
+
const hostnames = hostnamesStr ? hostnamesStr.split(',').map((h) => h.trim()) : [];
|
|
1858
|
+
return { ip: ip.trim(), hostnames };
|
|
1859
|
+
})
|
|
1860
|
+
: [];
|
|
1861
|
+
const hostAliasesYaml =
|
|
1862
|
+
hostAliases.length > 0
|
|
1863
|
+
? ` hostAliases:\n${hostAliases
|
|
1864
|
+
.map(
|
|
1865
|
+
(alias) =>
|
|
1866
|
+
` - ip: "${alias.ip}"\n hostnames:\n${alias.hostnames.map((h) => ` - "${h}"`).join('\n')}`,
|
|
1867
|
+
)
|
|
1868
|
+
.join('\n')}`
|
|
1869
|
+
: '';
|
|
1843
1870
|
const labels = options.labels
|
|
1844
1871
|
? options.labels
|
|
1845
1872
|
.split(',')
|
|
@@ -1870,6 +1897,7 @@ spec:
|
|
|
1870
1897
|
restartPolicy: ${restartPolicy}
|
|
1871
1898
|
${runtimeClassName ? ` runtimeClassName: ${runtimeClassName}` : ''}
|
|
1872
1899
|
${hostNetwork ? ` hostNetwork: ${hostNetwork}` : ''}
|
|
1900
|
+
${hostAliasesYaml}
|
|
1873
1901
|
containers:
|
|
1874
1902
|
- name: ${containerName}
|
|
1875
1903
|
image: ${imageName}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import { Css } from './components/core/Css.js';
|
|
4
|
+
import { Responsive } from './components/core/Responsive.js';
|
|
5
|
+
import { TranslateCore } from './components/core/Translate.js';
|
|
6
|
+
import { LogInUnderpost } from './components/underpost/LogInUnderpost.js';
|
|
7
|
+
import { LogOutUnderpost } from './components/underpost/LogOutUnderpost.js';
|
|
8
|
+
import { SignUpUnderpost } from './components/underpost/SignUpUnderpost.js';
|
|
9
|
+
import { MenuUnderpost } from './components/underpost/MenuUnderpost.js';
|
|
10
|
+
import { RouterUnderpost } from './components/underpost/RoutesUnderpost.js';
|
|
11
|
+
import { TranslateUnderpost } from './components/underpost/TranslateUnderpost.js';
|
|
12
|
+
import { Worker } from './components/core/Worker.js';
|
|
13
|
+
import { UnderpostParams } from './components/underpost/CommonUnderpost.js';
|
|
14
|
+
import { Keyboard } from './components/core/Keyboard.js';
|
|
15
|
+
import { SocketIoUnderpost } from './components/underpost/SocketIoUnderpost.js';
|
|
16
|
+
import { SocketIo } from './components/core/SocketIo.js';
|
|
17
|
+
import { ElementsUnderpost } from './components/underpost/ElementsUnderpost.js';
|
|
18
|
+
import { CssUnderpostDark, CssUnderpostLight } from './components/underpost/CssUnderpost.js';
|
|
19
|
+
|
|
20
|
+
window.onload = () =>
|
|
21
|
+
Worker.instance({
|
|
22
|
+
router: RouterUnderpost,
|
|
23
|
+
render: async () => {
|
|
24
|
+
await Css.loadThemes([CssUnderpostDark, CssUnderpostLight]);
|
|
25
|
+
await TranslateCore.Init();
|
|
26
|
+
await TranslateUnderpost.Init();
|
|
27
|
+
await Responsive.Init();
|
|
28
|
+
await MenuUnderpost.Render();
|
|
29
|
+
await SocketIo.Init({ channels: ElementsUnderpost.Data });
|
|
30
|
+
await SocketIoUnderpost.Init();
|
|
31
|
+
await LogInUnderpost();
|
|
32
|
+
await LogOutUnderpost();
|
|
33
|
+
await SignUpUnderpost();
|
|
34
|
+
await Keyboard.Init({ callBackTime: UnderpostParams.EVENT_CALLBACK_TIME });
|
|
35
|
+
},
|
|
36
|
+
});
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
coreUI,
|
|
24
24
|
sanitizeRoute,
|
|
25
25
|
getQueryParams,
|
|
26
|
+
setRouterReady,
|
|
26
27
|
} from './Router.js';
|
|
27
28
|
import { NotificationManager } from './NotificationManager.js';
|
|
28
29
|
import { EventsUI } from './EventsUI.js';
|
|
@@ -1401,6 +1402,7 @@ const Modal = {
|
|
|
1401
1402
|
}
|
|
1402
1403
|
});
|
|
1403
1404
|
setTimeout(window.onresize);
|
|
1405
|
+
setRouterReady();
|
|
1404
1406
|
});
|
|
1405
1407
|
})();
|
|
1406
1408
|
break;
|
|
@@ -196,7 +196,7 @@ const PublicProfile = {
|
|
|
196
196
|
user: { _id: userId, username },
|
|
197
197
|
} = options;
|
|
198
198
|
const idModal = options.idModal || getId();
|
|
199
|
-
const profileId = `public-profile-${
|
|
199
|
+
const profileId = `public-profile-${username}`;
|
|
200
200
|
const waveAnimationId = `${profileId}-wave`;
|
|
201
201
|
const profileImageClass = `${profileId}-image`;
|
|
202
202
|
const profileContainerId = `${profileId}-container`;
|
|
@@ -673,7 +673,7 @@ const PublicProfile = {
|
|
|
673
673
|
"
|
|
674
674
|
>
|
|
675
675
|
<div
|
|
676
|
-
class="${profileId}-image-container"
|
|
676
|
+
class="${profileId}-image-container public-profile-image-container"
|
|
677
677
|
style="
|
|
678
678
|
position: relative;
|
|
679
679
|
width: 160px;
|
|
@@ -688,7 +688,7 @@ const PublicProfile = {
|
|
|
688
688
|
"
|
|
689
689
|
>
|
|
690
690
|
<img
|
|
691
|
-
class="${profileImageClass}"
|
|
691
|
+
class="${profileImageClass} public-profile-image"
|
|
692
692
|
style="
|
|
693
693
|
width: 100%;
|
|
694
694
|
height: 100%;
|
|
@@ -39,6 +39,36 @@ const coreUI = ['modal-menu', 'main-body', 'main-body-top', 'bottom-bar', 'board
|
|
|
39
39
|
*/
|
|
40
40
|
const closeModalRouteChangeEvents = {};
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Deferred promise that resolves once the full UI (including deferred slide-menu DOM
|
|
44
|
+
* setup in Modal) is ready. Any code that depends on the complete DOM — route handlers,
|
|
45
|
+
* session callbacks, panel updates, etc. — can simply `await RouterReady` instead of
|
|
46
|
+
* scattering individual null-checks across every microfrontend.
|
|
47
|
+
*
|
|
48
|
+
* Resolved by calling `setRouterReady()`, which should happen exactly once at the end
|
|
49
|
+
* of Modal's deferred slide-menu `setTimeout` block.
|
|
50
|
+
* @type {Promise<void>}
|
|
51
|
+
* @memberof PwaRouter
|
|
52
|
+
*/
|
|
53
|
+
let _routerReadyResolve;
|
|
54
|
+
const RouterReady = new Promise((resolve) => {
|
|
55
|
+
_routerReadyResolve = resolve;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Signals that the deferred UI setup is complete and the router (and any other
|
|
60
|
+
* awaiter of `RouterReady`) may safely access the full DOM.
|
|
61
|
+
* This must be called exactly once – typically at the end of Modal's deferred
|
|
62
|
+
* slide-menu `setTimeout` block.
|
|
63
|
+
* @memberof PwaRouter
|
|
64
|
+
*/
|
|
65
|
+
const setRouterReady = () => {
|
|
66
|
+
if (_routerReadyResolve) {
|
|
67
|
+
_routerReadyResolve();
|
|
68
|
+
_routerReadyResolve = undefined;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
42
72
|
/**
|
|
43
73
|
* Determines the base path for the application, often used for routing within a sub-directory.
|
|
44
74
|
* It checks the current URL's pathname and `window.Routes` to return the appropriate proxy path.
|
|
@@ -209,7 +239,8 @@ const Router = function (options = { Routes: () => {}, e: new PopStateEvent() })
|
|
|
209
239
|
* @param {object} RouterInstance - The router instance configuration, including the `Routes` function.
|
|
210
240
|
* @memberof PwaRouter
|
|
211
241
|
*/
|
|
212
|
-
const LoadRouter = function (RouterInstance) {
|
|
242
|
+
const LoadRouter = async function (RouterInstance) {
|
|
243
|
+
await RouterReady;
|
|
213
244
|
Router(RouterInstance);
|
|
214
245
|
window.onpopstate = (e) => {
|
|
215
246
|
Router({ ...RouterInstance, e });
|
|
@@ -466,4 +497,6 @@ export {
|
|
|
466
497
|
sanitizeRoute,
|
|
467
498
|
queryParamsChangeListeners,
|
|
468
499
|
listenQueryParamsChange,
|
|
500
|
+
setRouterReady,
|
|
501
|
+
RouterReady,
|
|
469
502
|
};
|
|
@@ -135,7 +135,7 @@ class PwaWorker {
|
|
|
135
135
|
const isInstall = await this.status();
|
|
136
136
|
if (!isInstall) await this.install();
|
|
137
137
|
await render();
|
|
138
|
-
LoadRouter(this.RouterInstance);
|
|
138
|
+
await LoadRouter(this.RouterInstance);
|
|
139
139
|
LoadingAnimation.removeSplashScreen();
|
|
140
140
|
if (this.devMode()) {
|
|
141
141
|
// const delayLiveReload = 1250;
|
|
@@ -30,6 +30,7 @@ const CssCommonCryptokoyn = async () => {
|
|
|
30
30
|
--cy-font-retro: 'retro-font';
|
|
31
31
|
--cy-font-retro-title: 'retro-font-title';
|
|
32
32
|
--cy-font-retro-sensitive: 'retro-font-sensitive';
|
|
33
|
+
--cy-font-retro-cta: 'retro-font-cta';
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
@font-face {
|
|
@@ -44,6 +45,64 @@ const CssCommonCryptokoyn = async () => {
|
|
|
44
45
|
font-family: 'retro-font-sensitive';
|
|
45
46
|
src: URL('${getProxyPath()}assets/fonts/VT323-Regular.ttf') format('truetype');
|
|
46
47
|
}
|
|
48
|
+
@font-face {
|
|
49
|
+
font-family: 'retro-font-cta';
|
|
50
|
+
src: URL('${getProxyPath()}assets/fonts/PressStart2P-Regular.ttf') format('truetype');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* Landing Page & Object Viewer Styles */
|
|
54
|
+
.landing-container {
|
|
55
|
+
display: flex;
|
|
56
|
+
flex-direction: column;
|
|
57
|
+
align-items: center;
|
|
58
|
+
justify-content: center;
|
|
59
|
+
height: 100vh;
|
|
60
|
+
width: 100%;
|
|
61
|
+
background: #000;
|
|
62
|
+
color: #fff;
|
|
63
|
+
text-align: center;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.landing-title,
|
|
67
|
+
h1,
|
|
68
|
+
h2,
|
|
69
|
+
h3 {
|
|
70
|
+
font-family: var(--cy-font-retro-cta);
|
|
71
|
+
font-size: 5rem;
|
|
72
|
+
color: #ff0d0d;
|
|
73
|
+
text-shadow: 2px 2px 0px #9e0808;
|
|
74
|
+
margin-bottom: 2rem;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
p {
|
|
78
|
+
font-family: var(--cy-font-retro);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.object-layer-viewer-container {
|
|
82
|
+
width: 100% !important;
|
|
83
|
+
font-family: var(--cy-font-retro);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.cta-button {
|
|
87
|
+
font-family: var(--cy-font-retro-cta);
|
|
88
|
+
font-size: 1.5rem;
|
|
89
|
+
padding: 1rem 2rem;
|
|
90
|
+
border: 3px solid #ff0d0d;
|
|
91
|
+
background: transparent;
|
|
92
|
+
color: #ff0d0d;
|
|
93
|
+
cursor: pointer;
|
|
94
|
+
transition: all 0.3s ease-in-out;
|
|
95
|
+
text-shadow: 1px 1px 0px #9e0808;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.cta-button:hover {
|
|
99
|
+
background: #ff0d0d;
|
|
100
|
+
color: #000;
|
|
101
|
+
box-shadow:
|
|
102
|
+
0 0 20px #ff0d0d,
|
|
103
|
+
0 0 40px #ff0d0d;
|
|
104
|
+
text-shadow: none;
|
|
105
|
+
}
|
|
47
106
|
|
|
48
107
|
/* Base typography and smoothing */
|
|
49
108
|
|
|
@@ -86,6 +145,9 @@ const CssCommonCryptokoyn = async () => {
|
|
|
86
145
|
.main-btn-menu {
|
|
87
146
|
font-size: 20px;
|
|
88
147
|
}
|
|
148
|
+
.input-container {
|
|
149
|
+
width: 278px;
|
|
150
|
+
}
|
|
89
151
|
</style>
|
|
90
152
|
|
|
91
153
|
<div class="ag-grid-style"></div>`;
|
|
@@ -137,7 +199,7 @@ const CssCryptokoynLight = {
|
|
|
137
199
|
}
|
|
138
200
|
.default-slide-menu-top-bar-fix-title-container-text {
|
|
139
201
|
font-size: 40px !important;
|
|
140
|
-
color:
|
|
202
|
+
color: #ffcc00 !important;
|
|
141
203
|
}
|
|
142
204
|
</style>
|
|
143
205
|
${borderChar(1, `#010101`, ['.default-slide-menu-top-bar-fix-title-container-text'])}
|
|
@@ -192,6 +192,16 @@ const ObjectLayerEngineModal = {
|
|
|
192
192
|
const { Elements } = options;
|
|
193
193
|
|
|
194
194
|
const directionCodes = ['08', '18', '02', '12', '04', '14', '06', '16'];
|
|
195
|
+
const directionCodeLabels = {
|
|
196
|
+
'08': 'Down Idle',
|
|
197
|
+
18: 'Down Walk',
|
|
198
|
+
'02': 'Up Idle',
|
|
199
|
+
12: 'Up Walk',
|
|
200
|
+
'04': 'Left Idle',
|
|
201
|
+
14: 'Left Walk',
|
|
202
|
+
'06': 'Right Idle',
|
|
203
|
+
16: 'Right Walk',
|
|
204
|
+
};
|
|
195
205
|
const itemTypes = ['skin', 'weapon', 'armor', 'artifact', 'floor'];
|
|
196
206
|
const statTypes = ['effect', 'resistance', 'agility', 'range', 'intelligence', 'utility'];
|
|
197
207
|
|
|
@@ -355,7 +365,7 @@ const ObjectLayerEngineModal = {
|
|
|
355
365
|
const capturedDirectionCode = directionCode;
|
|
356
366
|
|
|
357
367
|
if (!s(`.frames-${capturedDirectionCode}`)) {
|
|
358
|
-
|
|
368
|
+
console.warn(`Frames container for direction code ${capturedDirectionCode} not found`);
|
|
359
369
|
return;
|
|
360
370
|
}
|
|
361
371
|
|
|
@@ -481,7 +491,7 @@ const ObjectLayerEngineModal = {
|
|
|
481
491
|
let loader = s('object-layer-png-loader');
|
|
482
492
|
|
|
483
493
|
if (!ole || !loader) {
|
|
484
|
-
|
|
494
|
+
console.warn('object-layer-engine or object-layer-png-loader component not found after retries');
|
|
485
495
|
return;
|
|
486
496
|
}
|
|
487
497
|
|
|
@@ -516,7 +526,7 @@ const ObjectLayerEngineModal = {
|
|
|
516
526
|
<div class="in section-mp-border">
|
|
517
527
|
<div class="fl">
|
|
518
528
|
<div class="in fll">
|
|
519
|
-
<div class="in direction-code-bar-frames-title">${directionCode}</div>
|
|
529
|
+
<div class="in direction-code-bar-frames-title">${directionCodeLabels[directionCode]}</div>
|
|
520
530
|
<div class="in direction-code-bar-frames-btn">
|
|
521
531
|
${await BtnIcon.Render({
|
|
522
532
|
label: html`
|
|
@@ -564,139 +574,153 @@ const ObjectLayerEngineModal = {
|
|
|
564
574
|
}
|
|
565
575
|
|
|
566
576
|
setTimeout(async () => {
|
|
577
|
+
let loadFramesInProgress = false;
|
|
567
578
|
const loadFrames = async () => {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
for (const directionCode of directionCodes) {
|
|
573
|
-
// Clear DOM frames for this direction code
|
|
574
|
-
const framesContainer = s(`.frames-${directionCode}`);
|
|
575
|
-
if (framesContainer) {
|
|
576
|
-
framesContainer.innerHTML = '';
|
|
577
|
-
}
|
|
578
|
-
// Clear data for this direction code
|
|
579
|
-
ObjectLayerEngineModal.ObjectLayerData[directionCode] = [];
|
|
579
|
+
// Concurrency guard: skip if already loading to prevent duplicate frames
|
|
580
|
+
if (loadFramesInProgress) {
|
|
581
|
+
console.warn('loadFrames already in progress, skipping duplicate call');
|
|
582
|
+
return;
|
|
580
583
|
}
|
|
584
|
+
loadFramesInProgress = true;
|
|
585
|
+
|
|
586
|
+
try {
|
|
587
|
+
showFrameLoading();
|
|
588
|
+
|
|
589
|
+
// Clear all frames and data at the start to prevent duplication from multiple calls
|
|
590
|
+
// This must happen BEFORE any async operations to avoid race conditions
|
|
591
|
+
for (const directionCode of directionCodes) {
|
|
592
|
+
// Clear DOM frames for this direction code
|
|
593
|
+
const framesContainer = s(`.frames-${directionCode}`);
|
|
594
|
+
if (framesContainer) {
|
|
595
|
+
framesContainer.innerHTML = '';
|
|
596
|
+
}
|
|
597
|
+
// Clear data for this direction code
|
|
598
|
+
ObjectLayerEngineModal.ObjectLayerData[directionCode] = [];
|
|
599
|
+
}
|
|
581
600
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
console.log(`Found ${frameCount} frames for direction: ${direction} (code: ${currentDirectionCode})`);
|
|
607
|
-
for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
|
608
|
-
const pngUrl = `${getProxyPath()}assets/${type}/${id}/${currentDirectionCode}/${frameIndex}.png`;
|
|
601
|
+
for (const directionCode of directionCodes) {
|
|
602
|
+
// Use IIFE to properly capture directionCode and handle async operations
|
|
603
|
+
await (async (currentDirectionCode) => {
|
|
604
|
+
// Register frame add button handler after DOM is ready
|
|
605
|
+
// Wait longer to ensure all direction bars are rendered
|
|
606
|
+
|
|
607
|
+
if (loadedData && loadedData.metadata && loadedData.metadata.data && currentDirectionCode) {
|
|
608
|
+
// Show loading animation only once on first direction that has frames
|
|
609
|
+
|
|
610
|
+
const { type, id } = loadedData.metadata.data.item;
|
|
611
|
+
const directions = ObjectLayerEngineModal.getDirectionsFromDirectionCode(currentDirectionCode);
|
|
612
|
+
|
|
613
|
+
console.log(`Loading frames for direction code: ${currentDirectionCode}, directions:`, directions);
|
|
614
|
+
|
|
615
|
+
// Check if frames exist for any direction mapped to this direction code
|
|
616
|
+
const { frames } = loadedData.objectLayerRenderFramesId;
|
|
617
|
+
for (const direction of directions) {
|
|
618
|
+
if (frames[direction] && frames[direction].length > 0) {
|
|
619
|
+
// Track this direction code as having original data
|
|
620
|
+
if (!ObjectLayerEngineModal.originalDirectionCodes.includes(currentDirectionCode)) {
|
|
621
|
+
ObjectLayerEngineModal.originalDirectionCodes.push(currentDirectionCode);
|
|
622
|
+
}
|
|
623
|
+
// Load frames from static PNG URLs sequentially to avoid race conditions
|
|
624
|
+
const frameCount = frames[direction].length;
|
|
609
625
|
console.log(
|
|
610
|
-
`
|
|
626
|
+
`Found ${frameCount} frames for direction: ${direction} (code: ${currentDirectionCode})`,
|
|
611
627
|
);
|
|
612
|
-
|
|
628
|
+
for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
|
629
|
+
const pngUrl = `${getProxyPath()}assets/${type}/${id}/${currentDirectionCode}/${frameIndex}.png`;
|
|
630
|
+
console.log(
|
|
631
|
+
`Loading frame ${frameIndex} for direction code ${currentDirectionCode} from: ${pngUrl}`,
|
|
632
|
+
);
|
|
633
|
+
await processAndAddFrameFromPngUrl(currentDirectionCode, pngUrl);
|
|
634
|
+
}
|
|
635
|
+
console.log(`Completed loading ${frameCount} frames for direction code: ${currentDirectionCode}`);
|
|
636
|
+
// Once we found frames for this direction code, we can break to avoid duplicates
|
|
637
|
+
break;
|
|
613
638
|
}
|
|
614
|
-
console.log(`Completed loading ${frameCount} frames for direction code: ${currentDirectionCode}`);
|
|
615
|
-
// Once we found frames for this direction code, we can break to avoid duplicates
|
|
616
|
-
break;
|
|
617
639
|
}
|
|
618
640
|
}
|
|
619
|
-
}
|
|
620
641
|
|
|
621
|
-
|
|
622
|
-
|
|
642
|
+
const buttonSelector = `.direction-code-bar-frames-btn-${currentDirectionCode}`;
|
|
643
|
+
console.log(`Registering click handler for: ${buttonSelector}`);
|
|
623
644
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
}
|
|
631
|
-
const image = await ole.toBlob();
|
|
632
|
-
const json = ole.exportMatrixJSON();
|
|
633
|
-
|
|
634
|
-
// Check if we're in edit mode
|
|
635
|
-
if (editingFrameId && editingDirectionCode) {
|
|
636
|
-
// Ensure we're clicking the add button for the same direction being edited
|
|
637
|
-
if (currentDirectionCode !== editingDirectionCode) {
|
|
638
|
-
NotificationManager.Push({
|
|
639
|
-
html: `<i class="fa-solid fa-exclamation-circle"></i> Please click the glowing <i class="fa-solid fa-edit"></i> button for direction <strong>${editingDirectionCode}</strong> to save changes, or click <i class="fa-solid fa-times"></i> to cancel.`,
|
|
640
|
-
status: 'warning',
|
|
641
|
-
});
|
|
642
|
-
return; // Don't add a new frame
|
|
645
|
+
EventsUI.onClick(buttonSelector, async () => {
|
|
646
|
+
console.log(`Add frame button clicked for direction: ${currentDirectionCode}`);
|
|
647
|
+
const ole = s('object-layer-engine');
|
|
648
|
+
if (!ole) {
|
|
649
|
+
console.error('object-layer-engine not found');
|
|
650
|
+
return;
|
|
643
651
|
}
|
|
652
|
+
const image = await ole.toBlob();
|
|
653
|
+
const json = ole.exportMatrixJSON();
|
|
654
|
+
|
|
655
|
+
// Check if we're in edit mode
|
|
656
|
+
if (editingFrameId && editingDirectionCode) {
|
|
657
|
+
// Ensure we're clicking the add button for the same direction being edited
|
|
658
|
+
if (currentDirectionCode !== editingDirectionCode) {
|
|
659
|
+
NotificationManager.Push({
|
|
660
|
+
html: `<i class="fa-solid fa-exclamation-circle"></i> Please click the glowing <i class="fa-solid fa-edit"></i> button for direction <strong>${editingDirectionCode}</strong> to save changes, or click <i class="fa-solid fa-times"></i> to cancel.`,
|
|
661
|
+
status: 'warning',
|
|
662
|
+
});
|
|
663
|
+
return; // Don't add a new frame
|
|
664
|
+
}
|
|
644
665
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
666
|
+
// UPDATE existing frame
|
|
667
|
+
console.log(`Updating frame ${editingFrameId} in direction ${editingDirectionCode}`);
|
|
668
|
+
|
|
669
|
+
// Find the frame in the data array
|
|
670
|
+
const frameArray = ObjectLayerEngineModal.ObjectLayerData[editingDirectionCode];
|
|
671
|
+
const frameIndex = frameArray?.findIndex((frame) => frame.id === editingFrameId);
|
|
672
|
+
|
|
673
|
+
if (frameIndex !== undefined && frameIndex >= 0) {
|
|
674
|
+
// Update the frame data while preserving the ID and index
|
|
675
|
+
frameArray[frameIndex] = {
|
|
676
|
+
id: editingFrameId,
|
|
677
|
+
image,
|
|
678
|
+
json,
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
// Update the visual representation
|
|
682
|
+
const imgElement = s(`.direction-code-bar-frames-img-${editingFrameId}`);
|
|
683
|
+
if (imgElement) {
|
|
684
|
+
imgElement.src = URL.createObjectURL(image);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
console.log(`Frame ${editingFrameId} updated successfully at index ${frameIndex}`);
|
|
688
|
+
NotificationManager.Push({
|
|
689
|
+
html: `<i class="fa-solid fa-check-circle"></i> Frame updated successfully at position ${frameIndex + 1}!`,
|
|
690
|
+
status: 'success',
|
|
691
|
+
});
|
|
692
|
+
} else {
|
|
693
|
+
console.error(`Could not find frame ${editingFrameId} in direction ${editingDirectionCode}`);
|
|
694
|
+
NotificationManager.Push({
|
|
695
|
+
html: `<i class="fa-solid fa-exclamation-triangle"></i> Error: Could not find frame to update`,
|
|
696
|
+
status: 'error',
|
|
697
|
+
});
|
|
664
698
|
}
|
|
665
699
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
html: `<i class="fa-solid fa-check-circle"></i> Frame updated successfully at position ${frameIndex + 1}!`,
|
|
669
|
-
status: 'success',
|
|
670
|
-
});
|
|
700
|
+
// Exit edit mode and restore UI
|
|
701
|
+
exitEditMode();
|
|
671
702
|
} else {
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
703
|
+
// ADD new frame (existing behavior)
|
|
704
|
+
const id = `frame-capture-${s4()}-${s4()}`;
|
|
705
|
+
console.log(`Creating new frame ${id} for direction ${currentDirectionCode}`);
|
|
706
|
+
|
|
707
|
+
if (!ObjectLayerEngineModal.ObjectLayerData[currentDirectionCode])
|
|
708
|
+
ObjectLayerEngineModal.ObjectLayerData[currentDirectionCode] = [];
|
|
709
|
+
ObjectLayerEngineModal.ObjectLayerData[currentDirectionCode].push({ id, image, json });
|
|
710
|
+
console.log(
|
|
711
|
+
`Stored frame ${id} in direction code ${currentDirectionCode}. Total frames:`,
|
|
712
|
+
ObjectLayerEngineModal.ObjectLayerData[currentDirectionCode].length,
|
|
713
|
+
);
|
|
678
714
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
ObjectLayerEngineModal.ObjectLayerData[currentDirectionCode] = [];
|
|
688
|
-
ObjectLayerEngineModal.ObjectLayerData[currentDirectionCode].push({ id, image, json });
|
|
689
|
-
console.log(
|
|
690
|
-
`Stored frame ${id} in direction code ${currentDirectionCode}. Total frames:`,
|
|
691
|
-
ObjectLayerEngineModal.ObjectLayerData[currentDirectionCode].length,
|
|
692
|
-
);
|
|
693
|
-
|
|
694
|
-
await addFrameToBar(currentDirectionCode, id, image, json);
|
|
695
|
-
}
|
|
696
|
-
});
|
|
697
|
-
})(directionCode);
|
|
715
|
+
await addFrameToBar(currentDirectionCode, id, image, json);
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
})(directionCode);
|
|
719
|
+
}
|
|
720
|
+
hideFrameLoading();
|
|
721
|
+
} finally {
|
|
722
|
+
loadFramesInProgress = false;
|
|
698
723
|
}
|
|
699
|
-
hideFrameLoading();
|
|
700
724
|
};
|
|
701
725
|
RouterEvents[`router-${options.idModal}`] = loadFrames;
|
|
702
726
|
|
|
@@ -748,6 +772,7 @@ const ObjectLayerEngineModal = {
|
|
|
748
772
|
data: {
|
|
749
773
|
stats: {},
|
|
750
774
|
item: {},
|
|
775
|
+
ledger: { type: 'OFF_CHAIN' },
|
|
751
776
|
},
|
|
752
777
|
};
|
|
753
778
|
for (const directionCode of directionCodes) {
|
|
@@ -980,6 +1005,7 @@ const ObjectLayerEngineModal = {
|
|
|
980
1005
|
font-weight: bold;
|
|
981
1006
|
font-size: 1.2rem;
|
|
982
1007
|
padding: 0.5rem;
|
|
1008
|
+
width: 70px;
|
|
983
1009
|
}
|
|
984
1010
|
.direction-code-bar-frames-img {
|
|
985
1011
|
width: 100px;
|