underpost 2.8.86 → 2.8.87
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/.env.development +6 -1
- package/.env.production +6 -1
- package/.env.test +6 -1
- package/README.md +22 -2
- package/bin/build.js +1 -0
- package/bin/deploy.js +32 -20
- package/bin/file.js +5 -2
- package/bin/util.js +1 -56
- package/cli.md +10 -5
- package/conf.js +3 -3
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/mongo-express/deployment.yaml +12 -12
- package/manifests/maas/nvim.sh +91 -0
- package/package.json +3 -11
- package/src/api/file/file.service.js +28 -8
- package/src/api/user/user.router.js +31 -5
- package/src/api/user/user.service.js +3 -4
- package/src/cli/cluster.js +4 -23
- package/src/cli/db.js +0 -19
- package/src/cli/deploy.js +21 -29
- package/src/cli/fs.js +1 -0
- package/src/cli/index.js +4 -1
- package/src/cli/repository.js +4 -2
- package/src/cli/run.js +17 -2
- package/src/client/components/core/CssCore.js +12 -0
- package/src/client/components/core/FullScreen.js +19 -28
- package/src/client/components/core/Input.js +1 -0
- package/src/client/components/core/Modal.js +25 -39
- package/src/client/components/core/ObjectLayerEngine.js +229 -4
- package/src/client/components/core/ObjectLayerEngineModal.js +441 -0
- package/src/client/components/core/ToggleSwitch.js +15 -1
- package/src/client/public/default/assets/mailer/api-user-default-avatar.png +0 -0
- package/src/index.js +1 -1
- package/src/server/client-build-docs.js +1 -1
- package/src/server/client-build.js +4 -12
- package/src/server/client-icons.js +6 -78
- package/src/server/conf.js +83 -138
- package/src/server/proxy.js +1 -1
- package/src/server/runtime.js +1 -2
- package/src/server/start.js +2 -2
- package/test/api.test.js +3 -2
- package/bin/cyberia0.js +0 -78
|
@@ -33,6 +33,22 @@ const templateHTML = html`
|
|
|
33
33
|
top: 0;
|
|
34
34
|
pointer-events: none;
|
|
35
35
|
}
|
|
36
|
+
.toolbar {
|
|
37
|
+
display: flex;
|
|
38
|
+
gap: 8px;
|
|
39
|
+
flex-wrap: wrap;
|
|
40
|
+
align-items: center;
|
|
41
|
+
}
|
|
42
|
+
.toolbar label {
|
|
43
|
+
display: inline-flex;
|
|
44
|
+
gap: 6px;
|
|
45
|
+
align-items: center;
|
|
46
|
+
}
|
|
47
|
+
.group {
|
|
48
|
+
display: inline-flex;
|
|
49
|
+
gap: 6px;
|
|
50
|
+
align-items: center;
|
|
51
|
+
}
|
|
36
52
|
</style>
|
|
37
53
|
|
|
38
54
|
<div class="wrap">
|
|
@@ -48,8 +64,34 @@ const templateHTML = html`
|
|
|
48
64
|
<label>brush <input type="number" part="brush-size" min="1" value="1" /></label>
|
|
49
65
|
<label>pixel-size <input type="number" part="pixel-size" min="1" value="16" /></label>
|
|
50
66
|
|
|
67
|
+
<!-- New: cell dimensions (width x height) -->
|
|
68
|
+
<label
|
|
69
|
+
>cells <input type="number" part="cell-width" min="1" value="16" style="width:6ch" /> x
|
|
70
|
+
<input type="number" part="cell-height" min="1" value="16" style="width:6ch"
|
|
71
|
+
/></label>
|
|
72
|
+
|
|
51
73
|
<label class="switch"> <input type="checkbox" part="toggle-grid" /> grid </label>
|
|
52
74
|
|
|
75
|
+
<!-- New: transform tools -->
|
|
76
|
+
<div class="group">
|
|
77
|
+
<button part="flip-h" title="Flip horizontally">Flip H</button>
|
|
78
|
+
<button part="flip-v" title="Flip vertically">Flip V</button>
|
|
79
|
+
<button part="rot-ccw" title="Rotate -90°">⟲</button>
|
|
80
|
+
<button part="rot-cw" title="Rotate +90°">⟳</button>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<label
|
|
84
|
+
>opacity <input type="range" part="opacity" min="0" max="255" value="255" style="width:10rem" /><input
|
|
85
|
+
type="number"
|
|
86
|
+
part="opacity-num"
|
|
87
|
+
min="0"
|
|
88
|
+
max="255"
|
|
89
|
+
value="255"
|
|
90
|
+
style="width:5ch;margin-left:4px"
|
|
91
|
+
/></label>
|
|
92
|
+
|
|
93
|
+
<button part="clear" title="Clear (make fully transparent)">Clear</button>
|
|
94
|
+
|
|
53
95
|
<button part="export">Export PNG</button>
|
|
54
96
|
<button part="export-json">Export JSON</button>
|
|
55
97
|
<button part="import-json">Import JSON</button>
|
|
@@ -79,11 +121,24 @@ class ObjectLayerEngineElement extends HTMLElement {
|
|
|
79
121
|
this._importJsonBtn = this.shadowRoot.querySelector('button[part="import-json"]');
|
|
80
122
|
this._toggleGrid = this.shadowRoot.querySelector('input[part="toggle-grid"]');
|
|
81
123
|
|
|
124
|
+
// new controls
|
|
125
|
+
this._widthInput = this.shadowRoot.querySelector('input[part="cell-width"]');
|
|
126
|
+
this._heightInput = this.shadowRoot.querySelector('input[part="cell-height"]');
|
|
127
|
+
this._flipHBtn = this.shadowRoot.querySelector('button[part="flip-h"]');
|
|
128
|
+
this._flipVBtn = this.shadowRoot.querySelector('button[part="flip-v"]');
|
|
129
|
+
this._rotCCWBtn = this.shadowRoot.querySelector('button[part="rot-ccw"]');
|
|
130
|
+
this._rotCWBtn = this.shadowRoot.querySelector('button[part="rot-cw"]');
|
|
131
|
+
this._clearBtn = this.shadowRoot.querySelector('button[part="clear"]');
|
|
132
|
+
this._opacityRange = this.shadowRoot.querySelector('input[part="opacity"]');
|
|
133
|
+
this._opacityNumber = this.shadowRoot.querySelector('input[part="opacity-num"]');
|
|
134
|
+
|
|
82
135
|
// internal state
|
|
83
136
|
this._width = 16;
|
|
84
137
|
this._height = 16;
|
|
85
138
|
this._pixelSize = 16;
|
|
86
139
|
this._brushSize = 1;
|
|
140
|
+
// brush color stored as [r,g,b,a]
|
|
141
|
+
this._brushColor = [0, 0, 0, 255];
|
|
87
142
|
this._matrix = this._createEmptyMatrix(this._width, this._height);
|
|
88
143
|
|
|
89
144
|
this._pixelCtx = null;
|
|
@@ -91,13 +146,18 @@ class ObjectLayerEngineElement extends HTMLElement {
|
|
|
91
146
|
|
|
92
147
|
this._isPointerDown = false;
|
|
93
148
|
this._tool = 'pencil';
|
|
94
|
-
this._brushColor = [0, 0, 0, 255];
|
|
95
149
|
this._showGrid = false;
|
|
96
150
|
|
|
97
151
|
// binds
|
|
98
152
|
this._onPointerDown = this._onPointerDown.bind(this);
|
|
99
153
|
this._onPointerMove = this._onPointerMove.bind(this);
|
|
100
154
|
this._onPointerUp = this._onPointerUp.bind(this);
|
|
155
|
+
|
|
156
|
+
// transform methods bound (useful if passing as callbacks)
|
|
157
|
+
this.flipHorizontal = this.flipHorizontal.bind(this);
|
|
158
|
+
this.flipVertical = this.flipVertical.bind(this);
|
|
159
|
+
this.rotateCW = this.rotateCW.bind(this);
|
|
160
|
+
this.rotateCCW = this.rotateCCW.bind(this);
|
|
101
161
|
}
|
|
102
162
|
|
|
103
163
|
static get observedAttributes() {
|
|
@@ -111,16 +171,29 @@ class ObjectLayerEngineElement extends HTMLElement {
|
|
|
111
171
|
}
|
|
112
172
|
|
|
113
173
|
connectedCallback() {
|
|
174
|
+
// respect attributes if present
|
|
114
175
|
if (this.hasAttribute('width')) this._width = Math.max(1, parseInt(this.getAttribute('width'), 10));
|
|
115
176
|
if (this.hasAttribute('height')) this._height = Math.max(1, parseInt(this.getAttribute('height'), 10));
|
|
116
177
|
if (this.hasAttribute('pixel-size')) this._pixelSize = Math.max(1, parseInt(this.getAttribute('pixel-size'), 10));
|
|
117
178
|
|
|
118
179
|
this._setupContextsAndSize();
|
|
119
180
|
|
|
181
|
+
// set initial UI control values (keeps in sync with attributes)
|
|
182
|
+
if (this._widthInput) this._widthInput.value = String(this._width);
|
|
183
|
+
if (this._heightInput) this._heightInput.value = String(this._height);
|
|
184
|
+
if (this._pixelSizeInput) this._pixelSizeInput.value = String(this._pixelSize);
|
|
185
|
+
if (this._brushSizeInput) this._brushSizeInput.value = String(this._brushSize);
|
|
186
|
+
|
|
187
|
+
// initialize color & opacity UI
|
|
188
|
+
if (this._colorInput) this._colorInput.value = this._rgbaToHex(this._brushColor);
|
|
189
|
+
if (this._opacityRange) this._opacityRange.value = String(this._brushColor[3]);
|
|
190
|
+
if (this._opacityNumber) this._opacityNumber.value = String(this._brushColor[3]);
|
|
191
|
+
|
|
120
192
|
// UI events
|
|
121
193
|
this._colorInput.addEventListener('input', (e) => {
|
|
122
|
-
const
|
|
123
|
-
|
|
194
|
+
const rgb = this._hexToRgba(e.target.value);
|
|
195
|
+
// keep current alpha
|
|
196
|
+
this.setBrushColor([rgb[0], rgb[1], rgb[2], this._brushColor[3]]);
|
|
124
197
|
});
|
|
125
198
|
this._toolSelect.addEventListener('change', (e) => this.setTool(e.target.value));
|
|
126
199
|
this._brushSizeInput.addEventListener('change', (e) => this.setBrushSize(parseInt(e.target.value, 10) || 1));
|
|
@@ -132,6 +205,42 @@ class ObjectLayerEngineElement extends HTMLElement {
|
|
|
132
205
|
this._renderGrid();
|
|
133
206
|
});
|
|
134
207
|
|
|
208
|
+
// opacity controls - keep range and number in sync
|
|
209
|
+
if (this._opacityRange) {
|
|
210
|
+
this._opacityRange.addEventListener('input', (e) => {
|
|
211
|
+
const v = Math.max(0, Math.min(255, parseInt(e.target.value, 10) || 0));
|
|
212
|
+
this.setBrushAlpha(v);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
if (this._opacityNumber) {
|
|
216
|
+
this._opacityNumber.addEventListener('change', (e) => {
|
|
217
|
+
const v = Math.max(0, Math.min(255, parseInt(e.target.value, 10) || 0));
|
|
218
|
+
this.setBrushAlpha(v);
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// width/height change -> resize (preserve existing content)
|
|
223
|
+
if (this._widthInput)
|
|
224
|
+
this._widthInput.addEventListener('change', (e) => {
|
|
225
|
+
const val = Math.max(1, parseInt(e.target.value, 10) || 1);
|
|
226
|
+
// keep value synced (will update input again in resize)
|
|
227
|
+
this.resize(val, this._height, { preserve: true });
|
|
228
|
+
});
|
|
229
|
+
if (this._heightInput)
|
|
230
|
+
this._heightInput.addEventListener('change', (e) => {
|
|
231
|
+
const val = Math.max(1, parseInt(e.target.value, 10) || 1);
|
|
232
|
+
this.resize(this._width, val, { preserve: true });
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// transform buttons
|
|
236
|
+
if (this._flipHBtn) this._flipHBtn.addEventListener('click', this.flipHorizontal);
|
|
237
|
+
if (this._flipVBtn) this._flipVBtn.addEventListener('click', this.flipVertical);
|
|
238
|
+
if (this._rotCWBtn) this._rotCWBtn.addEventListener('click', this.rotateCW);
|
|
239
|
+
if (this._rotCCWBtn) this._rotCCWBtn.addEventListener('click', this.rotateCCW);
|
|
240
|
+
|
|
241
|
+
// clear button (makes canvas fully transparent)
|
|
242
|
+
if (this._clearBtn) this._clearBtn.addEventListener('click', () => this.clear([0, 0, 0, 0]));
|
|
243
|
+
|
|
135
244
|
// Export/Import
|
|
136
245
|
this._exportBtn.addEventListener('click', () => this.exportPNG());
|
|
137
246
|
this._exportJsonBtn.addEventListener('click', () => {
|
|
@@ -169,6 +278,14 @@ class ObjectLayerEngineElement extends HTMLElement {
|
|
|
169
278
|
this._pixelCanvas.removeEventListener('pointerdown', this._onPointerDown);
|
|
170
279
|
window.removeEventListener('pointermove', this._onPointerMove);
|
|
171
280
|
window.removeEventListener('pointerup', this._onPointerUp);
|
|
281
|
+
|
|
282
|
+
if (this._flipHBtn) this._flipHBtn.removeEventListener('click', this.flipHorizontal);
|
|
283
|
+
if (this._flipVBtn) this._flipVBtn.removeEventListener('click', this.flipVertical);
|
|
284
|
+
if (this._rotCWBtn) this._rotCWBtn.removeEventListener('click', this.rotateCW);
|
|
285
|
+
if (this._rotCCWBtn) this._rotCCWBtn.removeEventListener('click', this.rotateCCW);
|
|
286
|
+
if (this._clearBtn) this._clearBtn.removeEventListener('click', () => this.clear([0, 0, 0, 0]));
|
|
287
|
+
if (this._opacityRange) this._opacityRange.removeEventListener('input', () => {});
|
|
288
|
+
if (this._opacityNumber) this._opacityNumber.removeEventListener('change', () => {});
|
|
172
289
|
}
|
|
173
290
|
|
|
174
291
|
// ---------------- Matrix helpers ----------------
|
|
@@ -227,6 +344,13 @@ class ObjectLayerEngineElement extends HTMLElement {
|
|
|
227
344
|
this._width = nw;
|
|
228
345
|
this._height = nh;
|
|
229
346
|
this._matrix = newMat;
|
|
347
|
+
|
|
348
|
+
// keep inputs and attributes in sync
|
|
349
|
+
if (this._widthInput) this._widthInput.value = String(this._width);
|
|
350
|
+
if (this._heightInput) this._heightInput.value = String(this._height);
|
|
351
|
+
this.setAttribute('width', String(this._width));
|
|
352
|
+
this.setAttribute('height', String(this._height));
|
|
353
|
+
|
|
230
354
|
this._setupContextsAndSize();
|
|
231
355
|
this.render();
|
|
232
356
|
this.dispatchEvent(new CustomEvent('resize', { detail: { width: nw, height: nh } }));
|
|
@@ -365,10 +489,33 @@ class ObjectLayerEngineElement extends HTMLElement {
|
|
|
365
489
|
this._tool = name;
|
|
366
490
|
if (this._toolSelect) this._toolSelect.value = name;
|
|
367
491
|
}
|
|
492
|
+
|
|
493
|
+
// set full RGBA brush color (alpha optional)
|
|
368
494
|
setBrushColor(rgba) {
|
|
369
|
-
|
|
495
|
+
if (!Array.isArray(rgba) || rgba.length < 3) return;
|
|
496
|
+
const r = this._clampInt(rgba[0]);
|
|
497
|
+
const g = this._clampInt(rgba[1]);
|
|
498
|
+
const b = this._clampInt(rgba[2]);
|
|
499
|
+
const a = typeof rgba[3] === 'number' ? this._clampInt(rgba[3]) : this._brushColor[3];
|
|
500
|
+
this._brushColor = [r, g, b, a];
|
|
370
501
|
if (this._colorInput) this._colorInput.value = this._rgbaToHex(this._brushColor);
|
|
502
|
+
if (this._opacityRange) this._opacityRange.value = String(this._brushColor[3]);
|
|
503
|
+
if (this._opacityNumber) this._opacityNumber.value = String(this._brushColor[3]);
|
|
371
504
|
}
|
|
505
|
+
|
|
506
|
+
// set brush alpha (0-255)
|
|
507
|
+
setBrushAlpha(a) {
|
|
508
|
+
const v = Math.max(0, Math.min(255, Math.floor(Number(a) || 0)));
|
|
509
|
+
this._brushColor[3] = v;
|
|
510
|
+
if (this._opacityRange) this._opacityRange.value = String(v);
|
|
511
|
+
if (this._opacityNumber) this._opacityNumber.value = String(v);
|
|
512
|
+
// keep color input (hex) representing rgb only
|
|
513
|
+
if (this._colorInput) this._colorInput.value = this._rgbaToHex(this._brushColor);
|
|
514
|
+
}
|
|
515
|
+
getBrushAlpha() {
|
|
516
|
+
return this._brushColor[3];
|
|
517
|
+
}
|
|
518
|
+
|
|
372
519
|
setBrushSize(n) {
|
|
373
520
|
this._brushSize = Math.max(1, Math.floor(n));
|
|
374
521
|
if (this._brushSizeInput) this._brushSizeInput.value = this._brushSize;
|
|
@@ -584,6 +731,84 @@ class ObjectLayerEngineElement extends HTMLElement {
|
|
|
584
731
|
.slice(1)}`;
|
|
585
732
|
}
|
|
586
733
|
|
|
734
|
+
// ---------------- Transform helpers (flip/rotate) ----------------
|
|
735
|
+
flipHorizontal() {
|
|
736
|
+
// reverse each row (mirror horizontally)
|
|
737
|
+
for (let y = 0; y < this._height; y++) {
|
|
738
|
+
this._matrix[y].reverse();
|
|
739
|
+
}
|
|
740
|
+
this.render();
|
|
741
|
+
this.dispatchEvent(new CustomEvent('transform', { detail: { type: 'flip-horizontal' } }));
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
flipVertical() {
|
|
745
|
+
// reverse the order of rows (mirror vertically)
|
|
746
|
+
this._matrix.reverse();
|
|
747
|
+
this.render();
|
|
748
|
+
this.dispatchEvent(new CustomEvent('transform', { detail: { type: 'flip-vertical' } }));
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
rotateCW() {
|
|
752
|
+
// rotate +90 degrees (clockwise)
|
|
753
|
+
const oldH = this._height;
|
|
754
|
+
const oldW = this._width;
|
|
755
|
+
const newW = oldH;
|
|
756
|
+
const newH = oldW;
|
|
757
|
+
const newMat = this._createEmptyMatrix(newW, newH);
|
|
758
|
+
for (let y = 0; y < oldH; y++) {
|
|
759
|
+
for (let x = 0; x < oldW; x++) {
|
|
760
|
+
const px = this._matrix[y][x] ? this._matrix[y][x].slice() : [0, 0, 0, 0];
|
|
761
|
+
const newX = oldH - 1 - y; // column in new matrix
|
|
762
|
+
const newY = x; // row in new matrix
|
|
763
|
+
newMat[newY][newX] = px;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
this._width = newW;
|
|
767
|
+
this._height = newH;
|
|
768
|
+
this._matrix = newMat;
|
|
769
|
+
// keep inputs/attributes in sync
|
|
770
|
+
if (this._widthInput) this._widthInput.value = String(this._width);
|
|
771
|
+
if (this._heightInput) this._heightInput.value = String(this._height);
|
|
772
|
+
this.setAttribute('width', String(this._width));
|
|
773
|
+
this.setAttribute('height', String(this._height));
|
|
774
|
+
|
|
775
|
+
this._setupContextsAndSize();
|
|
776
|
+
this.render();
|
|
777
|
+
this.dispatchEvent(
|
|
778
|
+
new CustomEvent('transform', { detail: { type: 'rotate-cw', width: this._width, height: this._height } }),
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
rotateCCW() {
|
|
783
|
+
// rotate -90 degrees (counter-clockwise)
|
|
784
|
+
const oldH = this._height;
|
|
785
|
+
const oldW = this._width;
|
|
786
|
+
const newW = oldH;
|
|
787
|
+
const newH = oldW;
|
|
788
|
+
const newMat = this._createEmptyMatrix(newW, newH);
|
|
789
|
+
for (let y = 0; y < oldH; y++) {
|
|
790
|
+
for (let x = 0; x < oldW; x++) {
|
|
791
|
+
const px = this._matrix[y][x] ? this._matrix[y][x].slice() : [0, 0, 0, 0];
|
|
792
|
+
const newX = y; // column in new matrix
|
|
793
|
+
const newY = oldW - 1 - x; // row in new matrix
|
|
794
|
+
newMat[newY][newX] = px;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
this._width = newW;
|
|
798
|
+
this._height = newH;
|
|
799
|
+
this._matrix = newMat;
|
|
800
|
+
if (this._widthInput) this._widthInput.value = String(this._width);
|
|
801
|
+
if (this._heightInput) this._heightInput.value = String(this._height);
|
|
802
|
+
this.setAttribute('width', String(this._width));
|
|
803
|
+
this.setAttribute('height', String(this._height));
|
|
804
|
+
|
|
805
|
+
this._setupContextsAndSize();
|
|
806
|
+
this.render();
|
|
807
|
+
this.dispatchEvent(
|
|
808
|
+
new CustomEvent('transform', { detail: { type: 'rotate-ccw', width: this._width, height: this._height } }),
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
|
|
587
812
|
// ---------------- Properties ----------------
|
|
588
813
|
get width() {
|
|
589
814
|
return this._width;
|