@underpostnet/underpost 2.98.0 → 2.98.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/.vscode/settings.json +7 -8
- package/README.md +2 -2
- package/bin/build.js +21 -5
- package/bin/deploy.js +10 -0
- package/bin/file.js +2 -1
- package/bin/util.js +0 -17
- package/cli.md +2 -2
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/package.json +2 -4
- package/scripts/rocky-setup.sh +12 -39
- package/src/api/document/document.service.js +1 -1
- package/src/cli/cluster.js +5 -9
- package/src/cli/repository.js +9 -9
- package/src/cli/run.js +108 -106
- package/src/client/components/core/Content.js +54 -4
- package/src/client/components/core/FullScreen.js +202 -9
- package/src/client/components/core/Panel.js +91 -22
- package/src/client/components/core/PanelForm.js +5 -2
- package/src/client/components/core/Translate.js +8 -0
- package/src/client/components/core/VanillaJs.js +80 -29
- package/src/client/services/default/default.management.js +324 -136
- package/src/index.js +58 -20
- package/src/client/components/core/ObjectLayerEngine.js +0 -1520
- package/src/client/components/core/ObjectLayerEngineModal.js +0 -1245
- package/src/client/components/core/ObjectLayerEngineViewer.js +0 -880
- package/src/server/object-layer.js +0 -335
|
@@ -1,880 +0,0 @@
|
|
|
1
|
-
import { loggerFactory } from './Logger.js';
|
|
2
|
-
import { getProxyPath, listenQueryPathInstance, setPath, setQueryParams } from './Router.js';
|
|
3
|
-
import { ObjectLayerService } from '../../services/object-layer/object-layer.service.js';
|
|
4
|
-
import { NotificationManager } from './NotificationManager.js';
|
|
5
|
-
import { htmls, s } from './VanillaJs.js';
|
|
6
|
-
|
|
7
|
-
import { darkTheme, ThemeEvents } from './Css.js';
|
|
8
|
-
import { ObjectLayerManagement } from '../../services/object-layer/object-layer.management.js';
|
|
9
|
-
import { ObjectLayerEngineModal } from './ObjectLayerEngineModal.js';
|
|
10
|
-
import { Modal } from './Modal.js';
|
|
11
|
-
import { DefaultManagement } from '../../services/default/default.management.js';
|
|
12
|
-
import { AgGrid } from './AgGrid.js';
|
|
13
|
-
|
|
14
|
-
const logger = loggerFactory(import.meta);
|
|
15
|
-
|
|
16
|
-
const ObjectLayerEngineViewer = {
|
|
17
|
-
Data: {
|
|
18
|
-
objectLayer: null,
|
|
19
|
-
frameCounts: null,
|
|
20
|
-
currentDirection: 'down',
|
|
21
|
-
currentMode: 'idle',
|
|
22
|
-
webp: null,
|
|
23
|
-
isGenerating: false,
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
// Map user-friendly direction/mode to numeric direction codes
|
|
27
|
-
getDirectionCode: function (direction, mode) {
|
|
28
|
-
const key = `${direction}_${mode}`;
|
|
29
|
-
const directionCodeMap = {
|
|
30
|
-
down_idle: '08',
|
|
31
|
-
down_walking: '18',
|
|
32
|
-
up_idle: '02',
|
|
33
|
-
up_walking: '12',
|
|
34
|
-
left_idle: '04',
|
|
35
|
-
left_walking: '14',
|
|
36
|
-
right_idle: '06',
|
|
37
|
-
right_walking: '16',
|
|
38
|
-
};
|
|
39
|
-
return directionCodeMap[key] || null;
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
Render: async function ({ Elements }) {
|
|
43
|
-
const id = 'object-layer-engine-viewer';
|
|
44
|
-
|
|
45
|
-
Modal.Data[`modal-${id}`].onReloadModalListener[id] = async () => {
|
|
46
|
-
ObjectLayerEngineViewer.Reload({ Elements });
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
// Listen for cid query parameter
|
|
50
|
-
listenQueryPathInstance(
|
|
51
|
-
{
|
|
52
|
-
id: `${id}-query-listener`,
|
|
53
|
-
routeId: 'object-layer-engine-viewer',
|
|
54
|
-
event: async (cid) => {
|
|
55
|
-
if (cid) {
|
|
56
|
-
await this.loadObjectLayer(cid, Elements);
|
|
57
|
-
} else {
|
|
58
|
-
this.renderEmpty({ Elements });
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
'cid',
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
return html`
|
|
66
|
-
<div class="fl">
|
|
67
|
-
<div class="in ${id}" id="${id}">
|
|
68
|
-
<div class="in section-mp">
|
|
69
|
-
<div class="in">Loading object layer...</div>
|
|
70
|
-
</div>
|
|
71
|
-
</div>
|
|
72
|
-
</div>
|
|
73
|
-
`;
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
renderEmpty: async function ({ Elements }) {
|
|
77
|
-
const id = 'object-layer-engine-viewer';
|
|
78
|
-
const idModal = 'modal-object-layer-engine-viewer';
|
|
79
|
-
const serviceId = 'object-layer-engine-management';
|
|
80
|
-
const gridId = `${serviceId}-grid-${idModal}`;
|
|
81
|
-
if (s(`.${serviceId}-grid-${idModal}`) && AgGrid.grids[gridId])
|
|
82
|
-
await DefaultManagement.loadTable(idModal, { reload: true });
|
|
83
|
-
else
|
|
84
|
-
htmls(
|
|
85
|
-
`#${id}`,
|
|
86
|
-
await ObjectLayerManagement.RenderTable({
|
|
87
|
-
Elements,
|
|
88
|
-
idModal,
|
|
89
|
-
}),
|
|
90
|
-
);
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
loadObjectLayer: async function (objectLayerId, Elements) {
|
|
94
|
-
const id = 'object-layer-engine-viewer';
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
// Load metadata first
|
|
98
|
-
const { status: metaStatus, data: metadata } = await ObjectLayerService.getMetadata({ id: objectLayerId });
|
|
99
|
-
|
|
100
|
-
if (metaStatus !== 'success' || !metadata) {
|
|
101
|
-
throw new Error('Failed to load object layer metadata');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
this.Data.objectLayer = metadata;
|
|
105
|
-
|
|
106
|
-
// Load frame counts for all directions
|
|
107
|
-
const { status: frameStatus, data: frameData } = await ObjectLayerService.getFrameCounts({ id: objectLayerId });
|
|
108
|
-
|
|
109
|
-
if (frameStatus !== 'success' || !frameData) {
|
|
110
|
-
throw new Error('Failed to load frame counts');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
this.Data.frameCounts = frameData.frameCounts;
|
|
114
|
-
// Priority order for directions
|
|
115
|
-
const directions = ['down', 'up', 'left', 'right'];
|
|
116
|
-
// Priority order for modes
|
|
117
|
-
const modes = ['idle', 'walking'];
|
|
118
|
-
this.Data.currentDirection = 'down';
|
|
119
|
-
this.Data.currentMode = 'idle';
|
|
120
|
-
|
|
121
|
-
// Render the viewer UI
|
|
122
|
-
await this.renderViewer({ Elements });
|
|
123
|
-
|
|
124
|
-
// Generate WebP
|
|
125
|
-
await this.generateWebp();
|
|
126
|
-
} catch (error) {
|
|
127
|
-
logger.error('Error loading object layer:', error);
|
|
128
|
-
NotificationManager.Push({
|
|
129
|
-
html: `Failed to load object layer: ${error.message}`,
|
|
130
|
-
status: 'error',
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
htmls(
|
|
134
|
-
`#${id}`,
|
|
135
|
-
html`
|
|
136
|
-
<div class="in section-mp">
|
|
137
|
-
<div class="in">
|
|
138
|
-
<h3>Error</h3>
|
|
139
|
-
<p>Failed to load object layer. Please try again.</p>
|
|
140
|
-
</div>
|
|
141
|
-
</div>
|
|
142
|
-
`,
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
|
|
147
|
-
renderViewer: async function ({ Elements }) {
|
|
148
|
-
const id = 'object-layer-engine-viewer';
|
|
149
|
-
const { objectLayer, frameCounts } = this.Data;
|
|
150
|
-
|
|
151
|
-
if (!objectLayer || !frameCounts) return;
|
|
152
|
-
|
|
153
|
-
const itemType = objectLayer.data.item.type;
|
|
154
|
-
const itemId = objectLayer.data.item.id;
|
|
155
|
-
const itemDescription = objectLayer.data.item.description || '';
|
|
156
|
-
const itemActivable = objectLayer.data.item.activable || false;
|
|
157
|
-
|
|
158
|
-
// Get stats data
|
|
159
|
-
const stats = objectLayer.data.stats || {};
|
|
160
|
-
|
|
161
|
-
// Helper function to check if direction/mode has frames
|
|
162
|
-
const hasFrames = (direction, mode) => {
|
|
163
|
-
const numericCode = this.getDirectionCode(direction, mode);
|
|
164
|
-
return numericCode && frameCounts[numericCode] && frameCounts[numericCode] > 0;
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
// Helper function to get frame count
|
|
168
|
-
const getFrameCount = (direction, mode) => {
|
|
169
|
-
const numericCode = this.getDirectionCode(direction, mode);
|
|
170
|
-
return numericCode ? frameCounts[numericCode] || 0 : 0;
|
|
171
|
-
};
|
|
172
|
-
ThemeEvents[id] = () => {
|
|
173
|
-
if (!s(`.style-${id}`)) return;
|
|
174
|
-
htmls(
|
|
175
|
-
`.style-${id}`,
|
|
176
|
-
html` <style>
|
|
177
|
-
.object-layer-viewer-container {
|
|
178
|
-
max-width: 800px;
|
|
179
|
-
margin: 0 auto;
|
|
180
|
-
padding: 20px;
|
|
181
|
-
font-family: 'retro-font';
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
.viewer-header {
|
|
185
|
-
text-align: center;
|
|
186
|
-
margin-bottom: 30px;
|
|
187
|
-
padding-bottom: 20px;
|
|
188
|
-
border-bottom: 2px solid ${darkTheme ? '#444' : '#ddd'};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
.viewer-header h2 {
|
|
192
|
-
margin: 0 0 10px 0;
|
|
193
|
-
color: ${darkTheme ? '#fff' : '#333'};
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
.webp-display-area {
|
|
197
|
-
background: ${darkTheme ? '#2a2a2a' : '#f5f5f5'};
|
|
198
|
-
border: 2px solid ${darkTheme ? '#444' : '#ddd'};
|
|
199
|
-
border-radius: 12px;
|
|
200
|
-
padding: 30px;
|
|
201
|
-
margin-bottom: 30px;
|
|
202
|
-
display: flex;
|
|
203
|
-
justify-content: center;
|
|
204
|
-
align-items: center;
|
|
205
|
-
min-height: 300px;
|
|
206
|
-
height: auto;
|
|
207
|
-
max-height: 600px;
|
|
208
|
-
position: relative;
|
|
209
|
-
overflow: auto;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
.webp-canvas-container {
|
|
213
|
-
position: relative;
|
|
214
|
-
display: flex;
|
|
215
|
-
justify-content: center;
|
|
216
|
-
align-items: center;
|
|
217
|
-
width: 100%;
|
|
218
|
-
height: 100%;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
.webp-canvas-container canvas,
|
|
222
|
-
.webp-canvas-container img {
|
|
223
|
-
image-rendering: pixelated;
|
|
224
|
-
image-rendering: -moz-crisp-edges;
|
|
225
|
-
image-rendering: crisp-edges;
|
|
226
|
-
-ms-interpolation-mode: nearest-neighbor;
|
|
227
|
-
background: repeating-conic-gradient(#80808020 0% 25%, #fff0 0% 50%) 50% / 20px 20px;
|
|
228
|
-
border-radius: 8px;
|
|
229
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
230
|
-
max-width: 100%;
|
|
231
|
-
max-height: 540px;
|
|
232
|
-
width: auto !important;
|
|
233
|
-
height: auto !important;
|
|
234
|
-
object-fit: contain;
|
|
235
|
-
display: block;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
.webp-canvas-container canvas {
|
|
239
|
-
background: repeating-conic-gradient(#80808020 0% 25%, #fff0 0% 50%) 50% / 20px 20px;
|
|
240
|
-
min-width: 128px;
|
|
241
|
-
min-height: 128px;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
.webp-info-badge {
|
|
245
|
-
position: absolute;
|
|
246
|
-
bottom: 10px;
|
|
247
|
-
right: 10px;
|
|
248
|
-
background: rgba(0, 0, 0, 0.2);
|
|
249
|
-
color: ${darkTheme ? 'white' : 'black'};
|
|
250
|
-
padding: 6px 12px;
|
|
251
|
-
border-radius: 4px;
|
|
252
|
-
font-size: 12px;
|
|
253
|
-
font-family: monospace;
|
|
254
|
-
backdrop-filter: blur(4px);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
.webp-info-badge .info-label {
|
|
258
|
-
opacity: 0.7;
|
|
259
|
-
margin-right: 4px;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
.loading-overlay {
|
|
263
|
-
position: absolute;
|
|
264
|
-
top: 0;
|
|
265
|
-
left: 0;
|
|
266
|
-
right: 0;
|
|
267
|
-
bottom: 0;
|
|
268
|
-
background: rgba(0, 0, 0, 0.7);
|
|
269
|
-
display: flex;
|
|
270
|
-
justify-content: center;
|
|
271
|
-
align-items: center;
|
|
272
|
-
color: white;
|
|
273
|
-
border-radius: 8px;
|
|
274
|
-
z-index: 10;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
.controls-container {
|
|
278
|
-
display: flex;
|
|
279
|
-
flex-direction: column;
|
|
280
|
-
gap: 20px;
|
|
281
|
-
margin-bottom: 20px;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
.control-group {
|
|
285
|
-
background: ${darkTheme ? '#2a2a2a' : '#fff'};
|
|
286
|
-
border: 1px solid ${darkTheme ? '#444' : '#ddd'};
|
|
287
|
-
border-radius: 8px;
|
|
288
|
-
padding: 15px 20px;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
.control-group h4 {
|
|
292
|
-
margin: 0 0 15px 0;
|
|
293
|
-
color: ${darkTheme ? '#fff' : '#333'};
|
|
294
|
-
font-size: 20px;
|
|
295
|
-
text-transform: uppercase;
|
|
296
|
-
letter-spacing: 1px;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
.button-group {
|
|
300
|
-
display: flex;
|
|
301
|
-
gap: 10px;
|
|
302
|
-
flex-wrap: wrap;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
.control-btn {
|
|
306
|
-
flex: 1;
|
|
307
|
-
min-width: 80px;
|
|
308
|
-
padding: 12px 20px;
|
|
309
|
-
border: 2px solid ${darkTheme ? '#444' : '#ddd'};
|
|
310
|
-
background: ${darkTheme ? '#333' : '#f9f9f9'};
|
|
311
|
-
color: ${darkTheme ? '#fff' : '#333'};
|
|
312
|
-
border-radius: 6px;
|
|
313
|
-
cursor: pointer;
|
|
314
|
-
transition: all 0.2s ease;
|
|
315
|
-
font-size: 14px;
|
|
316
|
-
font-weight: 600;
|
|
317
|
-
display: flex;
|
|
318
|
-
align-items: center;
|
|
319
|
-
justify-content: center;
|
|
320
|
-
gap: 8px;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
.control-btn:hover {
|
|
324
|
-
background: ${darkTheme ? '#444' : '#f0f0f0'};
|
|
325
|
-
transform: translateY(-2px);
|
|
326
|
-
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
.control-btn.active {
|
|
330
|
-
background: ${darkTheme ? '#4a9eff' : '#2196F3'};
|
|
331
|
-
color: white;
|
|
332
|
-
border-color: ${darkTheme ? '#4a9eff' : '#2196F3'};
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
.control-btn i {
|
|
336
|
-
font-size: 16px;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
.default-viewer-btn {
|
|
340
|
-
width: 100%;
|
|
341
|
-
padding: 15px;
|
|
342
|
-
background: ${darkTheme ? '#4caf50' : '#4CAF50'};
|
|
343
|
-
color: white;
|
|
344
|
-
border: none;
|
|
345
|
-
border-radius: 8px;
|
|
346
|
-
cursor: pointer;
|
|
347
|
-
font-size: 16px;
|
|
348
|
-
font-weight: 600;
|
|
349
|
-
transition: all 0.2s ease;
|
|
350
|
-
display: flex;
|
|
351
|
-
align-items: center;
|
|
352
|
-
justify-content: center;
|
|
353
|
-
gap: 10px;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
.default-viewer-btn:hover {
|
|
357
|
-
background: ${darkTheme ? '#45a049' : '#45a049'};
|
|
358
|
-
transform: translateY(-2px);
|
|
359
|
-
box-shadow: 0 4px 12px rgba(76, 175, 80, 0.3);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
.control-btn:disabled {
|
|
363
|
-
background: ${darkTheme ? '#555' : '#ccc'};
|
|
364
|
-
cursor: not-allowed;
|
|
365
|
-
transform: none;
|
|
366
|
-
opacity: 0.5;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
.control-btn .frame-count {
|
|
370
|
-
font-size: 11px;
|
|
371
|
-
opacity: 0.7;
|
|
372
|
-
margin-left: 4px;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
.default-viewer-btn:disabled {
|
|
376
|
-
background: ${darkTheme ? '#555' : '#ccc'};
|
|
377
|
-
cursor: not-allowed;
|
|
378
|
-
transform: none;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
.edit-btn {
|
|
382
|
-
background: ${darkTheme ? '#4a9eff' : '#2196F3'};
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
.edit-btn:hover {
|
|
386
|
-
background: ${darkTheme ? '#3a8eff' : '#1186f2'};
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
@media (max-width: 768px) {
|
|
390
|
-
.webp-display-area {
|
|
391
|
-
max-height: 500px;
|
|
392
|
-
min-height: 300px;
|
|
393
|
-
padding: 20px;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
.webp-canvas-container canvas,
|
|
397
|
-
.webp-canvas-container img {
|
|
398
|
-
max-width: 100%;
|
|
399
|
-
max-height: 440px;
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
@media (max-width: 600px) {
|
|
404
|
-
.webp-display-area {
|
|
405
|
-
max-height: 400px;
|
|
406
|
-
min-height: 250px;
|
|
407
|
-
padding: 15px;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
.webp-canvas-container canvas,
|
|
411
|
-
.webp-canvas-container img {
|
|
412
|
-
max-height: 340px;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
.button-group {
|
|
416
|
-
flex-direction: column;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
.control-btn {
|
|
420
|
-
min-width: 100%;
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
.item-data-key-label {
|
|
424
|
-
font-size: 16px;
|
|
425
|
-
color: ${darkTheme ? '#aaa' : '#666'};
|
|
426
|
-
text-transform: uppercase;
|
|
427
|
-
}
|
|
428
|
-
.item-data-value-label {
|
|
429
|
-
font-size: 20px;
|
|
430
|
-
font-weight: 700;
|
|
431
|
-
color: ${darkTheme ? '#aaa' : '#666'};
|
|
432
|
-
text-align: center;
|
|
433
|
-
}
|
|
434
|
-
.item-stat-entry {
|
|
435
|
-
display: flex;
|
|
436
|
-
flex-direction: column;
|
|
437
|
-
gap: 6px;
|
|
438
|
-
padding: 12px;
|
|
439
|
-
background: ${darkTheme ? '#1a1a1a' : '#f9f9f9'};
|
|
440
|
-
border-radius: 6px;
|
|
441
|
-
border: 1px solid ${darkTheme ? '#333' : '#e0e0e0'};
|
|
442
|
-
}
|
|
443
|
-
.no-data-container {
|
|
444
|
-
grid-column: 1 / -1;
|
|
445
|
-
text-align: center;
|
|
446
|
-
color: ${darkTheme ? '#666' : '#999'};
|
|
447
|
-
padding: 20px;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
@media (max-width: 850px) {
|
|
451
|
-
.object-layer-viewer-container {
|
|
452
|
-
padding: 5px;
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
</style>`,
|
|
456
|
-
);
|
|
457
|
-
};
|
|
458
|
-
htmls(
|
|
459
|
-
`#${id}`,
|
|
460
|
-
html`
|
|
461
|
-
<div class="hide style-${id}"></div>
|
|
462
|
-
|
|
463
|
-
<div class="object-layer-viewer-container">
|
|
464
|
-
<!-- Item Data Section -->
|
|
465
|
-
<div class="control-group" style="margin-bottom: 20px;">
|
|
466
|
-
<h4><i class="fa-solid fa-cube"></i> Item Data</h4>
|
|
467
|
-
<div
|
|
468
|
-
style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; padding: 10px 0;"
|
|
469
|
-
>
|
|
470
|
-
<div style="display: flex; flex-direction: column; gap: 4px;">
|
|
471
|
-
<span class="item-data-key-label">Item ID</span>
|
|
472
|
-
<span style="font-weight: 600;">${itemId}</span>
|
|
473
|
-
</div>
|
|
474
|
-
<div style="display: flex; flex-direction: column; gap: 4px;">
|
|
475
|
-
<span class="item-data-key-label">Type</span>
|
|
476
|
-
<span style="font-weight: 600;">${itemType}</span>
|
|
477
|
-
</div>
|
|
478
|
-
${itemDescription
|
|
479
|
-
? html`<div style="display: flex; flex-direction: column; gap: 4px;">
|
|
480
|
-
<span class="item-data-key-label">Description</span>
|
|
481
|
-
<span style="font-weight: 600;">${itemDescription}</span>
|
|
482
|
-
</div>`
|
|
483
|
-
: ''}
|
|
484
|
-
<div style="display: flex; flex-direction: column; gap: 4px;">
|
|
485
|
-
<span class="item-data-key-label">Activable</span>
|
|
486
|
-
<span style="font-weight: 600;">${itemActivable ? 'Yes' : 'No'}</span>
|
|
487
|
-
</div>
|
|
488
|
-
</div>
|
|
489
|
-
</div>
|
|
490
|
-
|
|
491
|
-
<!-- Stats Data Section -->
|
|
492
|
-
<div class="control-group" style="margin-bottom: 20px;">
|
|
493
|
-
<h4><i class="fa-solid fa-chart-bar"></i> Stats Data</h4>
|
|
494
|
-
<div
|
|
495
|
-
style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 15px; padding: 10px 0;"
|
|
496
|
-
>
|
|
497
|
-
${Object.keys(stats).length > 0
|
|
498
|
-
? Object.entries(stats)
|
|
499
|
-
.map(([statKey, statValue]) => {
|
|
500
|
-
const statInfo = ObjectLayerEngineModal.statDescriptions[statKey];
|
|
501
|
-
if (!statInfo) return '';
|
|
502
|
-
return html`
|
|
503
|
-
<div class="item-stat-entry">
|
|
504
|
-
<div style="display: flex; align-items: center; gap: 8px;">
|
|
505
|
-
<i class="${statInfo.icon}" id="stat-icon-${statKey}-${id}"></i>
|
|
506
|
-
<span class="item-data-key-label">${statInfo.title}</span>
|
|
507
|
-
</div>
|
|
508
|
-
<span class="item-data-value-label">${statValue}</span>
|
|
509
|
-
</div>
|
|
510
|
-
`;
|
|
511
|
-
})
|
|
512
|
-
.join('')
|
|
513
|
-
: html`<div class="no-data-container">No stats data available</div>`}
|
|
514
|
-
</div>
|
|
515
|
-
</div>
|
|
516
|
-
|
|
517
|
-
<div class="webp-display-area">
|
|
518
|
-
<div class="webp-canvas-container" id="webp-canvas-container">
|
|
519
|
-
<div style="text-align: center; color: ${darkTheme ? '#aaa' : '#666'};">
|
|
520
|
-
<i class="fa-solid fa-image" style="font-size: 48px; opacity: 0.3; margin-bottom: 16px;"></i>
|
|
521
|
-
<p style="margin: 0; font-size: 14px;">WebP preview will appear here</p>
|
|
522
|
-
</div>
|
|
523
|
-
<div id="webp-loading-overlay" class="loading-overlay" style="display: none;">
|
|
524
|
-
<div>
|
|
525
|
-
<i class="fa-solid fa-spinner fa-spin"></i>
|
|
526
|
-
<span style="margin-left: 10px;">Generating WebP...</span>
|
|
527
|
-
</div>
|
|
528
|
-
</div>
|
|
529
|
-
</div>
|
|
530
|
-
</div>
|
|
531
|
-
|
|
532
|
-
<div class="controls-container">
|
|
533
|
-
<div class="control-group">
|
|
534
|
-
<h4><i class="fa-solid fa-compass"></i> Direction</h4>
|
|
535
|
-
<div class="button-group">
|
|
536
|
-
<button
|
|
537
|
-
class="control-btn ${this.Data.currentDirection === 'up' ? 'active' : ''}"
|
|
538
|
-
data-direction="up"
|
|
539
|
-
${!hasFrames('up', this.Data.currentMode) ? 'disabled' : ''}
|
|
540
|
-
>
|
|
541
|
-
<i class="fa-solid fa-arrow-up"></i>
|
|
542
|
-
<span>Up</span>
|
|
543
|
-
${hasFrames('up', this.Data.currentMode)
|
|
544
|
-
? html`<span class="frame-count">(${getFrameCount('up', this.Data.currentMode)})</span>`
|
|
545
|
-
: ''}
|
|
546
|
-
</button>
|
|
547
|
-
<button
|
|
548
|
-
class="control-btn ${this.Data.currentDirection === 'down' ? 'active' : ''}"
|
|
549
|
-
data-direction="down"
|
|
550
|
-
${!hasFrames('down', this.Data.currentMode) ? 'disabled' : ''}
|
|
551
|
-
>
|
|
552
|
-
<i class="fa-solid fa-arrow-down"></i>
|
|
553
|
-
<span>Down</span>
|
|
554
|
-
${hasFrames('down', this.Data.currentMode)
|
|
555
|
-
? html`<span class="frame-count">(${getFrameCount('down', this.Data.currentMode)})</span>`
|
|
556
|
-
: ''}
|
|
557
|
-
</button>
|
|
558
|
-
<button
|
|
559
|
-
class="control-btn ${this.Data.currentDirection === 'left' ? 'active' : ''}"
|
|
560
|
-
data-direction="left"
|
|
561
|
-
${!hasFrames('left', this.Data.currentMode) ? 'disabled' : ''}
|
|
562
|
-
>
|
|
563
|
-
<i class="fa-solid fa-arrow-left"></i>
|
|
564
|
-
<span>Left</span>
|
|
565
|
-
${hasFrames('left', this.Data.currentMode)
|
|
566
|
-
? html`<span class="frame-count">(${getFrameCount('left', this.Data.currentMode)})</span>`
|
|
567
|
-
: ''}
|
|
568
|
-
</button>
|
|
569
|
-
<button
|
|
570
|
-
class="control-btn ${this.Data.currentDirection === 'right' ? 'active' : ''}"
|
|
571
|
-
data-direction="right"
|
|
572
|
-
${!hasFrames('right', this.Data.currentMode) ? 'disabled' : ''}
|
|
573
|
-
>
|
|
574
|
-
<i class="fa-solid fa-arrow-right"></i>
|
|
575
|
-
<span>Right</span>
|
|
576
|
-
${hasFrames('right', this.Data.currentMode)
|
|
577
|
-
? html`<span class="frame-count">(${getFrameCount('right', this.Data.currentMode)})</span>`
|
|
578
|
-
: ''}
|
|
579
|
-
</button>
|
|
580
|
-
</div>
|
|
581
|
-
</div>
|
|
582
|
-
|
|
583
|
-
<div class="control-group">
|
|
584
|
-
<h4><i class="fa-solid fa-person-running"></i> Mode</h4>
|
|
585
|
-
<div class="button-group">
|
|
586
|
-
<button
|
|
587
|
-
class="control-btn ${this.Data.currentMode === 'idle' ? 'active' : ''}"
|
|
588
|
-
data-mode="idle"
|
|
589
|
-
${!hasFrames(this.Data.currentDirection, 'idle') ? 'disabled' : ''}
|
|
590
|
-
>
|
|
591
|
-
<i class="fa-solid fa-user"></i>
|
|
592
|
-
<span>Idle</span>
|
|
593
|
-
${hasFrames(this.Data.currentDirection, 'idle')
|
|
594
|
-
? html`<span class="frame-count">(${getFrameCount(this.Data.currentDirection, 'idle')})</span>`
|
|
595
|
-
: ''}
|
|
596
|
-
</button>
|
|
597
|
-
<button
|
|
598
|
-
class="control-btn ${this.Data.currentMode === 'walking' ? 'active' : ''}"
|
|
599
|
-
data-mode="walking"
|
|
600
|
-
${!hasFrames(this.Data.currentDirection, 'walking') ? 'disabled' : ''}
|
|
601
|
-
>
|
|
602
|
-
<i class="fa-solid fa-person-walking"></i>
|
|
603
|
-
<span>Walking</span>
|
|
604
|
-
${hasFrames(this.Data.currentDirection, 'walking')
|
|
605
|
-
? html`<span class="frame-count">(${getFrameCount(this.Data.currentDirection, 'walking')})</span>`
|
|
606
|
-
: ''}
|
|
607
|
-
</button>
|
|
608
|
-
</div>
|
|
609
|
-
</div>
|
|
610
|
-
</div>
|
|
611
|
-
|
|
612
|
-
<div style="display: flex; gap: 10px; margin-top: 20px;">
|
|
613
|
-
<button class="default-viewer-btn" id="return-to-list-btn">
|
|
614
|
-
<i class="fa-solid fa-arrow-left"></i>
|
|
615
|
-
<span>Return to List</span>
|
|
616
|
-
</button>
|
|
617
|
-
<button class="default-viewer-btn" id="download-webp-btn">
|
|
618
|
-
<i class="fa-solid fa-download"></i>
|
|
619
|
-
<span>Download WebP</span>
|
|
620
|
-
</button>
|
|
621
|
-
<button class="default-viewer-btn edit-btn" id="edit-object-layer-btn">
|
|
622
|
-
<i class="fa-solid fa-edit"></i>
|
|
623
|
-
<span>Edit</span>
|
|
624
|
-
</button>
|
|
625
|
-
</div>
|
|
626
|
-
</div>
|
|
627
|
-
`,
|
|
628
|
-
);
|
|
629
|
-
ThemeEvents[id]();
|
|
630
|
-
// Attach event listeners
|
|
631
|
-
this.attachEventListeners({ Elements });
|
|
632
|
-
},
|
|
633
|
-
|
|
634
|
-
attachEventListeners: function ({ Elements }) {
|
|
635
|
-
// Direction buttons
|
|
636
|
-
const directionButtons = document.querySelectorAll('[data-direction]');
|
|
637
|
-
directionButtons.forEach((btn) => {
|
|
638
|
-
btn.addEventListener('click', async (e) => {
|
|
639
|
-
if (e.currentTarget.disabled) return;
|
|
640
|
-
const direction = e.currentTarget.getAttribute('data-direction');
|
|
641
|
-
if (direction !== this.Data.currentDirection) {
|
|
642
|
-
this.Data.currentDirection = direction;
|
|
643
|
-
await this.renderViewer({ Elements });
|
|
644
|
-
await this.attachEventListeners({ Elements });
|
|
645
|
-
await this.generateWebp();
|
|
646
|
-
}
|
|
647
|
-
});
|
|
648
|
-
});
|
|
649
|
-
|
|
650
|
-
// Mode buttons
|
|
651
|
-
const modeButtons = document.querySelectorAll('[data-mode]');
|
|
652
|
-
modeButtons.forEach((btn) => {
|
|
653
|
-
btn.addEventListener('click', async (e) => {
|
|
654
|
-
if (e.currentTarget.disabled) return;
|
|
655
|
-
const mode = e.currentTarget.getAttribute('data-mode');
|
|
656
|
-
if (mode !== this.Data.currentMode) {
|
|
657
|
-
this.Data.currentMode = mode;
|
|
658
|
-
await this.renderViewer({ Elements });
|
|
659
|
-
await this.attachEventListeners({ Elements });
|
|
660
|
-
await this.generateWebp();
|
|
661
|
-
}
|
|
662
|
-
});
|
|
663
|
-
});
|
|
664
|
-
|
|
665
|
-
// Download button
|
|
666
|
-
const downloadBtn = s('#download-webp-btn');
|
|
667
|
-
if (downloadBtn) {
|
|
668
|
-
downloadBtn.addEventListener('click', () => {
|
|
669
|
-
this.downloadWebp();
|
|
670
|
-
});
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
// Return to list button
|
|
674
|
-
const listBtn = s('#return-to-list-btn');
|
|
675
|
-
if (listBtn) {
|
|
676
|
-
listBtn.addEventListener('click', () => {
|
|
677
|
-
setPath(`${getProxyPath()}object-layer-engine-viewer`);
|
|
678
|
-
setQueryParams({ cid: null });
|
|
679
|
-
ObjectLayerEngineViewer.renderEmpty({ Elements });
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
// Edit button
|
|
684
|
-
const editBtn = s('#edit-object-layer-btn');
|
|
685
|
-
if (editBtn) {
|
|
686
|
-
editBtn.addEventListener('click', () => {
|
|
687
|
-
this.toEngine();
|
|
688
|
-
});
|
|
689
|
-
}
|
|
690
|
-
},
|
|
691
|
-
|
|
692
|
-
generateWebp: async function () {
|
|
693
|
-
if (this.Data.isGenerating) return;
|
|
694
|
-
|
|
695
|
-
const { objectLayer, frameCounts, currentDirection, currentMode } = this.Data;
|
|
696
|
-
if (!objectLayer || !frameCounts) return;
|
|
697
|
-
|
|
698
|
-
// Get numeric direction code
|
|
699
|
-
const numericCode = this.getDirectionCode(currentDirection, currentMode);
|
|
700
|
-
if (!numericCode) {
|
|
701
|
-
NotificationManager.Push({
|
|
702
|
-
html: `Invalid direction/mode combination: ${currentDirection} ${currentMode}`,
|
|
703
|
-
status: 'error',
|
|
704
|
-
});
|
|
705
|
-
return;
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
const frameCount = frameCounts[numericCode];
|
|
709
|
-
|
|
710
|
-
if (!frameCount || frameCount === 0) {
|
|
711
|
-
NotificationManager.Push({
|
|
712
|
-
html: `No frames available for ${currentDirection} ${currentMode}`,
|
|
713
|
-
status: 'warning',
|
|
714
|
-
});
|
|
715
|
-
return;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
const itemType = objectLayer.data.item.type;
|
|
719
|
-
const itemId = objectLayer.data.item.id;
|
|
720
|
-
const frameDuration = objectLayer.data.render.frame_duration || 100;
|
|
721
|
-
|
|
722
|
-
this.Data.isGenerating = true;
|
|
723
|
-
this.showLoading(true);
|
|
724
|
-
|
|
725
|
-
// Update loading overlay text
|
|
726
|
-
const loadingOverlay = s('#webp-loading-overlay');
|
|
727
|
-
if (loadingOverlay) {
|
|
728
|
-
const loadingText = loadingOverlay.querySelector('span');
|
|
729
|
-
if (loadingText) {
|
|
730
|
-
loadingText.textContent = `Loading WebP animation for ${currentDirection} ${currentMode}...`;
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
try {
|
|
735
|
-
// Call the WebP generation API endpoint
|
|
736
|
-
const { status, data } = await ObjectLayerService.generateWebp({
|
|
737
|
-
itemType,
|
|
738
|
-
itemId,
|
|
739
|
-
directionCode: numericCode,
|
|
740
|
-
});
|
|
741
|
-
|
|
742
|
-
if (status === 'success' && data) {
|
|
743
|
-
// Store the blob URL
|
|
744
|
-
this.Data.webp = data;
|
|
745
|
-
|
|
746
|
-
// Display the WebP in the viewer
|
|
747
|
-
const container = s('#webp-canvas-container');
|
|
748
|
-
if (container) {
|
|
749
|
-
// Clear container
|
|
750
|
-
container.innerHTML = '';
|
|
751
|
-
|
|
752
|
-
// Create and append image
|
|
753
|
-
const img = document.createElement('img');
|
|
754
|
-
img.src = data;
|
|
755
|
-
img.alt = 'WebP Animation';
|
|
756
|
-
container.appendChild(img);
|
|
757
|
-
|
|
758
|
-
// Create and append info badge
|
|
759
|
-
const infoBadge = document.createElement('div');
|
|
760
|
-
infoBadge.className = 'webp-info-badge';
|
|
761
|
-
infoBadge.innerHTML = html`
|
|
762
|
-
<span class="info-label" style="margin-left: 8px;">Frames:</span>
|
|
763
|
-
<span>${frameCount}</span><br />
|
|
764
|
-
<span class="info-label" style="margin-left: 8px;">Duration:</span>
|
|
765
|
-
<span>${frameDuration}ms</span><br />
|
|
766
|
-
<span class="info-label" style="margin-left: 8px;">Direction:</span>
|
|
767
|
-
<span>${currentDirection}</span><br />
|
|
768
|
-
<span class="info-label" style="margin-left: 8px;">Mode:</span>
|
|
769
|
-
<span>${currentMode}</span><br />
|
|
770
|
-
<span class="info-label" style="margin-left: 8px;">Code:</span>
|
|
771
|
-
<span>${numericCode}</span>
|
|
772
|
-
`;
|
|
773
|
-
const displayArea = s('.webp-display-area');
|
|
774
|
-
if (displayArea) {
|
|
775
|
-
displayArea.appendChild(infoBadge);
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
// NotificationManager.Push({
|
|
780
|
-
// html: `WebP generated successfully (${frameCount} frames, ${frameDuration}ms duration)`,
|
|
781
|
-
// status: 'success',
|
|
782
|
-
// });
|
|
783
|
-
} else {
|
|
784
|
-
throw new Error('Failed to generate WebP');
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
this.Data.isGenerating = false;
|
|
788
|
-
this.showLoading(false);
|
|
789
|
-
} catch (error) {
|
|
790
|
-
logger.error('Error generating WebP:', error);
|
|
791
|
-
NotificationManager.Push({
|
|
792
|
-
html: `Failed to generate WebP: ${error.message}`,
|
|
793
|
-
status: 'error',
|
|
794
|
-
});
|
|
795
|
-
this.Data.isGenerating = false;
|
|
796
|
-
this.showLoading(false);
|
|
797
|
-
}
|
|
798
|
-
},
|
|
799
|
-
|
|
800
|
-
showLoading: function (show) {
|
|
801
|
-
const overlay = s('#webp-loading-overlay');
|
|
802
|
-
if (overlay) {
|
|
803
|
-
overlay.style.display = show ? 'flex' : 'none';
|
|
804
|
-
if (!show) {
|
|
805
|
-
// Reset loading text when hiding
|
|
806
|
-
const loadingText = overlay.querySelector('span');
|
|
807
|
-
if (loadingText) {
|
|
808
|
-
loadingText.textContent = 'Generating WebP...';
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
const downloadBtn = s('#download-webp-btn');
|
|
814
|
-
if (downloadBtn) {
|
|
815
|
-
downloadBtn.disabled = show;
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
// Remove old info badge if exists
|
|
819
|
-
const oldBadge = s('.webp-info-badge');
|
|
820
|
-
if (oldBadge && show) {
|
|
821
|
-
oldBadge.remove();
|
|
822
|
-
}
|
|
823
|
-
},
|
|
824
|
-
|
|
825
|
-
downloadWebp: function () {
|
|
826
|
-
if (!this.Data.webp) {
|
|
827
|
-
NotificationManager.Push({
|
|
828
|
-
html: 'No WebP available to download',
|
|
829
|
-
status: 'warning',
|
|
830
|
-
});
|
|
831
|
-
return;
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
const { objectLayer, currentDirection, currentMode } = this.Data;
|
|
835
|
-
const numericCode = this.getDirectionCode(currentDirection, currentMode);
|
|
836
|
-
const filename = `${objectLayer.data.item.id}_${currentDirection}_${currentMode}_${numericCode}.webp`;
|
|
837
|
-
|
|
838
|
-
// Create a temporary anchor element to trigger download
|
|
839
|
-
const a = document.createElement('a');
|
|
840
|
-
a.href = this.Data.webp;
|
|
841
|
-
a.download = filename;
|
|
842
|
-
document.body.appendChild(a);
|
|
843
|
-
a.click();
|
|
844
|
-
document.body.removeChild(a);
|
|
845
|
-
|
|
846
|
-
NotificationManager.Push({
|
|
847
|
-
html: `WebP downloaded: ${filename}`,
|
|
848
|
-
status: 'success',
|
|
849
|
-
});
|
|
850
|
-
},
|
|
851
|
-
|
|
852
|
-
toEngine: function () {
|
|
853
|
-
const { objectLayer } = this.Data;
|
|
854
|
-
if (!objectLayer || !objectLayer._id) return;
|
|
855
|
-
|
|
856
|
-
// Navigate to editor route first
|
|
857
|
-
setPath(`${getProxyPath()}object-layer-engine`);
|
|
858
|
-
// Then add query param without replacing history
|
|
859
|
-
setQueryParams({ cid: objectLayer._id }, { replace: true });
|
|
860
|
-
|
|
861
|
-
if (s(`.modal-object-layer-engine`)) {
|
|
862
|
-
ObjectLayerEngineModal.Reload();
|
|
863
|
-
} else {
|
|
864
|
-
s(`.main-btn-object-layer-engine`)?.click();
|
|
865
|
-
}
|
|
866
|
-
},
|
|
867
|
-
|
|
868
|
-
Reload: async function ({ Elements }) {
|
|
869
|
-
const queryParams = new URLSearchParams(window.location.search);
|
|
870
|
-
const cid = queryParams.get('cid');
|
|
871
|
-
|
|
872
|
-
if (cid) {
|
|
873
|
-
await this.loadObjectLayer(cid, Elements);
|
|
874
|
-
} else {
|
|
875
|
-
this.renderEmpty({ Elements });
|
|
876
|
-
}
|
|
877
|
-
},
|
|
878
|
-
};
|
|
879
|
-
|
|
880
|
-
export { ObjectLayerEngineViewer };
|