underpost 2.85.7 → 2.89.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/.github/workflows/release.cd.yml +3 -1
  2. package/README.md +2 -2
  3. package/bin/build.js +8 -10
  4. package/bin/index.js +8 -1
  5. package/cli.md +3 -2
  6. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  7. package/manifests/deployment/dd-test-development/deployment.yaml +50 -50
  8. package/manifests/deployment/dd-test-development/proxy.yaml +4 -4
  9. package/package.json +1 -1
  10. package/scripts/rpmfusion-ffmpeg-setup.sh +55 -0
  11. package/src/api/file/file.service.js +29 -3
  12. package/src/cli/baremetal.js +1 -2
  13. package/src/cli/index.js +1 -0
  14. package/src/cli/repository.js +8 -1
  15. package/src/cli/run.js +104 -36
  16. package/src/client/components/core/AgGrid.js +42 -3
  17. package/src/client/components/core/CommonJs.js +4 -0
  18. package/src/client/components/core/Css.js +95 -48
  19. package/src/client/components/core/CssCore.js +0 -1
  20. package/src/client/components/core/LoadingAnimation.js +2 -2
  21. package/src/client/components/core/Logger.js +2 -9
  22. package/src/client/components/core/Modal.js +22 -14
  23. package/src/client/components/core/ObjectLayerEngine.js +300 -9
  24. package/src/client/components/core/ObjectLayerEngineModal.js +686 -148
  25. package/src/client/components/core/ObjectLayerEngineViewer.js +1061 -0
  26. package/src/client/components/core/Pagination.js +57 -12
  27. package/src/client/components/core/Router.js +37 -1
  28. package/src/client/components/core/Translate.js +4 -0
  29. package/src/client/components/core/Worker.js +8 -1
  30. package/src/client/services/default/default.management.js +86 -16
  31. package/src/db/mariadb/MariaDB.js +2 -2
  32. package/src/index.js +1 -1
  33. package/src/server/client-build.js +57 -2
  34. package/src/server/object-layer.js +44 -0
  35. package/src/server/start.js +12 -0
  36. package/src/ws/IoInterface.js +2 -3
  37. package/AUTHORS.md +0 -21
  38. package/src/server/network.js +0 -72
package/src/cli/run.js CHANGED
@@ -58,6 +58,8 @@ class UnderpostRun {
58
58
  * @property {string} stdin - The stdin option for the container.
59
59
  * @property {string} restartPolicy - The restart policy for the container.
60
60
  * @property {boolean} terminal - Whether to open a terminal.
61
+ * @property {number} devProxyPortOffset - The port offset for the development proxy.
62
+ * @property {string} confServerPath - The configuration server path.
61
63
  * @memberof UnderpostRun
62
64
  */
63
65
  static DEFAULT_OPTION = {
@@ -80,6 +82,7 @@ class UnderpostRun {
80
82
  restartPolicy: '',
81
83
  terminal: false,
82
84
  devProxyPortOffset: 0,
85
+ confServerPath: '',
83
86
  };
84
87
  /**
85
88
  * @static
@@ -257,30 +260,6 @@ class UnderpostRun {
257
260
  shellExec(`node bin deploy --restore-hosts`);
258
261
  },
259
262
 
260
- /**
261
- * @method cyberia-ide
262
- * @description Starts the development environment (IDE) for both `cyberia-server` and `cyberia-client` repositories.
263
- * @param {string} path - The input value, identifier, or path for the operation.
264
- * @param {Object} options - The default underpost runner options for customizing workflow
265
- * @memberof UnderpostRun
266
- */
267
- 'cyberia-ide': (path, options = UnderpostRun.DEFAULT_OPTION) => {
268
- const baseCommand = options.dev ? 'node bin' : 'underpost';
269
- shellExec(`${baseCommand} run ide /home/dd/cyberia-server`);
270
- shellExec(`${baseCommand} run ide /home/dd/cyberia-client`);
271
- },
272
- /**
273
- * @method engine-ide
274
- * @description Starts the development environment (IDE) for the `engine` and `engine-private` repositories.
275
- * @param {string} path - The input value, identifier, or path for the operation.
276
- * @param {Object} options - The default underpost runner options for customizing workflow
277
- * @memberof UnderpostRun
278
- */
279
- 'engine-ide': (path, options = UnderpostRun.DEFAULT_OPTION) => {
280
- const baseCommand = options.dev ? 'node bin' : 'underpost';
281
- shellExec(`${baseCommand} run ide /home/dd/engine`);
282
- shellExec(`${baseCommand} run ide /home/dd/engine/engine-private`);
283
- },
284
263
  /**
285
264
  * @method cluster-build
286
265
  * @description Build configuration for cluster deployment.
@@ -293,8 +272,12 @@ class UnderpostRun {
293
272
  shellExec(`node bin run --dev sync-replica template-deploy`);
294
273
  shellExec(`node bin run sync-replica template-deploy`);
295
274
  shellExec(`node bin env clean`);
296
- shellExec(`git add . && underpost cmt . build cluster-build`);
297
- shellExec(`cd engine-private && git add . && underpost cmt . build cluster-build`);
275
+ for (const deployId of fs.readFileSync('./engine-private/deploy/dd.router', 'utf8').split(','))
276
+ shellExec(`node bin/deploy update-default-conf ${deployId.trim()}`);
277
+ if (path === 'cmt') {
278
+ shellExec(`git add . && underpost cmt . build cluster-build`);
279
+ shellExec(`cd engine-private && git add . && underpost cmt . build cluster-build`);
280
+ }
298
281
  },
299
282
  /**
300
283
  * @method template-deploy
@@ -423,6 +406,7 @@ class UnderpostRun {
423
406
  // Dev usage: node bin run --dev --build sync dd-default
424
407
  const env = options.dev ? 'development' : 'production';
425
408
  const baseCommand = options.dev ? 'node bin' : 'underpost';
409
+ const baseClusterCommand = options.dev ? ' --dev' : '';
426
410
  const defaultPath = [
427
411
  'dd-default',
428
412
  1,
@@ -440,6 +424,8 @@ class UnderpostRun {
440
424
  if (isDeployRunnerContext(path, options)) {
441
425
  const { validVersion } = UnderpostRepository.API.privateConfUpdate(deployId);
442
426
  if (!validVersion) throw new Error('Version mismatch');
427
+ shellExec(`${baseCommand} run${baseClusterCommand} tz`);
428
+ shellExec(`${baseCommand} run${baseClusterCommand} cron`);
443
429
  }
444
430
 
445
431
  const currentTraffic = isDeployRunnerContext(path, options)
@@ -461,6 +447,39 @@ class UnderpostRun {
461
447
  UnderpostDeploy.API.switchTraffic(deployId, env, targetTraffic);
462
448
  } else logger.info('current traffic', UnderpostDeploy.API.getCurrentTraffic(deployId));
463
449
  },
450
+
451
+ /**
452
+ * @method tz
453
+ * @description Sets the system timezone using `timedatectl set-timezone` command.
454
+ * @param {string} path - The input value, identifier, or path for the operation (used as the timezone string).
455
+ * @param {Object} options - The default underpost runner options for customizing workflow
456
+ * @memberof UnderpostRun
457
+ */
458
+ tz: (path, options = UnderpostRun.DEFAULT_OPTION) => {
459
+ const tz = path
460
+ ? path
461
+ : UnderpostRootEnv.API.get('TIME_ZONE', undefined, { disableLog: true })
462
+ ? UnderpostRootEnv.API.get('TIME_ZONE')
463
+ : process.env.TIME_ZONE
464
+ ? process.env.TIME_ZONE
465
+ : 'America/New_York';
466
+ shellExec(`sudo timedatectl set-timezone ${tz}`);
467
+ },
468
+
469
+ /**
470
+ * @method cron
471
+ * @description Sets up and starts the `dd-cron` environment by writing environment variables, starting the cron service, and cleaning up.
472
+ * @param {string} path - The input value, identifier, or path for the operation.
473
+ * @param {Object} options - The default underpost runner options for customizing workflow
474
+ * @memberof UnderpostRun
475
+ */
476
+ cron: (path, options = UnderpostRun.DEFAULT_OPTION) => {
477
+ const env = options.dev ? 'development' : 'production';
478
+ shellExec(`node bin env ${path ? path : 'dd-cron'} ${env}`);
479
+ shellExec(`npm start`);
480
+ shellExec(`node bin env clean`);
481
+ },
482
+
464
483
  /**
465
484
  * @method ls-deployments
466
485
  * @description Retrieves and logs a table of Kubernetes deployments using `UnderpostDeploy.API.get`.
@@ -806,6 +825,22 @@ class UnderpostRun {
806
825
  */
807
826
  dev: async (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
808
827
  let [deployId, subConf, host, _path, clientHostPort] = path.split(',');
828
+ if (options.confServerPath) {
829
+ const confServer = JSON.parse(fs.readFileSync(options.confServerPath, 'utf8'));
830
+ fs.writeFileSync(
831
+ `./engine-private/conf/${deployId}/conf.server.dev.${subConf}.json`,
832
+ JSON.stringify(
833
+ {
834
+ [host]: {
835
+ [_path]: confServer[host][_path],
836
+ },
837
+ },
838
+ null,
839
+ 4,
840
+ ),
841
+ 'utf8',
842
+ );
843
+ }
809
844
  if (!deployId) deployId = 'dd-default';
810
845
  if (!host) host = 'default.net';
811
846
  if (!_path) _path = '/';
@@ -820,7 +855,7 @@ class UnderpostRun {
820
855
  envObj.DEV_PROXY_PORT_OFFSET = options.devProxyPortOffset;
821
856
  writeEnv(envPath, envObj);
822
857
  }
823
- shellExec(`node bin run dev-cluster expose`);
858
+ shellExec(`node bin run dev-cluster expose`, { async: true });
824
859
  {
825
860
  const cmd = `npm run dev-api ${deployId} ${subConf} ${host} ${_path} ${clientHostPort}${options.tls ? ' tls' : ''}`;
826
861
  options.terminal ? openTerminal(cmd) : shellExec(cmd, { async: true });
@@ -896,6 +931,41 @@ class UnderpostRun {
896
931
  }
897
932
  },
898
933
 
934
+ /**
935
+ * @method sh
936
+ * @description Enables remote control for the Kitty terminal emulator.
937
+ * @param {string} path - The input value, identifier, or path for the operation.
938
+ * @param {Object} options - The default underpost runner options for customizing workflow
939
+ * @memberof UnderpostRun
940
+ */
941
+ sh: async (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
942
+ let [operator, arg0, arg1] = path.split(',');
943
+ if (operator == 'copy') {
944
+ shellExec(
945
+ `kitty @ get-text ${arg0 === 'all' ? '--match all' : '--self'} --extent all${arg1 === 'ansi' ? ' --ansi yes' : ''} | kitty +kitten clipboard`,
946
+ );
947
+ return;
948
+ }
949
+ shellExec(`kitty -o allow_remote_control=yes`);
950
+ },
951
+
952
+ /**
953
+ * @method log
954
+ * @description Searches and highlights keywords in a specified log file, optionally showing surrounding lines.
955
+ * @param {string} path - The input value, identifier, or path for the operation (formatted as `filePath,keywords,lines`).
956
+ * @param {Object} options - The default underpost runner options for customizing workflow
957
+ * @memberof UnderpostRun
958
+ */
959
+ log: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
960
+ const [filePath, keywords, lines] = path.split(',');
961
+ let result = shellExec(`grep -i -E ${lines ? `-C ${lines} ` : ''}'${keywords}' ${filePath}`, {
962
+ stdout: true,
963
+ silent: true,
964
+ }).replaceAll(`--`, `==============================`.green.bold);
965
+ for (const keyword of keywords.split('|')) result = result.replaceAll(keyword, keyword.bgYellow.black.bold);
966
+ console.log(result);
967
+ },
968
+
899
969
  /**
900
970
  * @method release-cmt
901
971
  * @description Commits and pushes a new release for the `engine` repository with a message indicating the new version.
@@ -927,15 +997,13 @@ class UnderpostRun {
927
997
  const _path = '/single-replica';
928
998
  const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
929
999
  shellExec(`${baseCommand} env ${deployId} ${env}`);
930
- for (const host of Object.keys(confServer)) {
931
- if (!(_path in confServer[host])) continue;
932
- shellExec(`node bin/deploy build-single-replica ${deployId} ${host} ${_path}`);
933
- shellExec(`node bin/deploy build-full-client ${deployId}`);
934
- const node = options.dev || !isDeployRunnerContext(path, options) ? 'kind-control-plane' : os.hostname();
935
- // deployId, replicas, versions, image, node
936
- let defaultPath = [deployId, 1, ``, ``, node];
937
- shellExec(`${baseCommand} run${options.dev === true ? ' --dev' : ''} --build sync ${defaultPath}`);
938
- }
1000
+ for (const host of Object.keys(confServer))
1001
+ if (_path in confServer[host]) shellExec(`node bin/deploy build-single-replica ${deployId} ${host} ${_path}`);
1002
+ const node = options.dev || !isDeployRunnerContext(path, options) ? 'kind-control-plane' : os.hostname();
1003
+ // deployId, replicas, versions, image, node
1004
+ let defaultPath = [deployId, 1, ``, ``, node];
1005
+ shellExec(`${baseCommand} run${options.dev === true ? ' --dev' : ''} --build sync ${defaultPath}`);
1006
+ shellExec(`node bin/deploy build-full-client ${deployId}`);
939
1007
  }
940
1008
  if (isDeployRunnerContext(path, options)) shellExec(`${baseCommand} run promote ${path} production`);
941
1009
  },
@@ -11,7 +11,7 @@ const AgGrid = {
11
11
  grids: {},
12
12
  theme: `ag-theme-alpine`, // quartz
13
13
  Render: async function (options) {
14
- let { id } = options;
14
+ let { id, paginationOptions } = options;
15
15
  setTimeout(() => {
16
16
  // Grid Options: Contains all of the grid configurations
17
17
  const gridOptions = {
@@ -34,6 +34,38 @@ const AgGrid = {
34
34
  return params.data && params.data._new;
35
35
  },
36
36
  },
37
+ // Cell rendering events
38
+ onFirstDataRendered: options?.onFirstDataRendered,
39
+ onViewportChanged: options?.onViewportChanged,
40
+ onModelUpdated: options?.onModelUpdated,
41
+ onVirtualRowRemoved: options?.onVirtualRowRemoved,
42
+ // Cell interaction events
43
+ onCellClicked: options?.onCellClicked,
44
+ onCellDoubleClicked: options?.onCellDoubleClicked,
45
+ onCellFocused: options?.onCellFocused,
46
+ onCellMouseOver: options?.onCellMouseOver,
47
+ onCellMouseOut: options?.onCellMouseOut,
48
+ onCellValueChanged: options?.onCellValueChanged,
49
+ // set background colour on every row, this is probably bad, should be using CSS classes
50
+ // rowStyle: { background: 'black' },
51
+
52
+ // set background colour on even rows again, this looks bad, should be using CSS classes
53
+ // getRowStyle: (params) => {
54
+ // if (params.node.rowIndex % 2 === 0) {
55
+ // return { background: 'red' };
56
+ // }
57
+ // },
58
+
59
+ // all rows assigned CSS class 'my-green-class'
60
+ // rowClass: 'my-green-class',
61
+
62
+ // all even rows assigned 'my-shaded-effect'
63
+ // getRowClass: (params) => {
64
+ // if (params.node.rowIndex % 2 === 0) {
65
+ // return 'my-shaded-effect';
66
+ // }
67
+ // },
68
+
37
69
  // domLayout: 'autoHeight', || 'normal'
38
70
  // Column Definitions: Defines & controls grid columns.
39
71
  columnDefs: options?.gridOptions?.rowData?.[0]
@@ -62,7 +94,9 @@ const AgGrid = {
62
94
  if (!options.style || !options.style.height) {
63
95
  if (options.parentModal && Modal.Data[options.parentModal].options.observer) {
64
96
  Modal.Data[options.parentModal].onObserverListener[id + '-observer'] = ({ width, height }) => {
65
- if (s(`.${id}`)) s(`.${id}`).style.height = `${height - 180}px`;
97
+ if (s(`.${id}`))
98
+ s(`.${id}`).style.height =
99
+ `${height - 180 + (options.customHeightOffset ? options.customHeightOffset : 0)}px`;
66
100
  else delete Modal.Data[options.parentModal].onObserverListener[id + '-observer'];
67
101
  };
68
102
  s(`.${id}`).style.height = `${s(`.${options.parentModal}`).offsetHeight - 180}px`;
@@ -70,6 +104,9 @@ const AgGrid = {
70
104
  }
71
105
  });
72
106
  const usePagination = options?.usePagination;
107
+ const limitOptionsAttr = paginationOptions?.limitOptions
108
+ ? `limit-options='${JSON.stringify(paginationOptions.limitOptions)}'`
109
+ : '';
73
110
  return html`
74
111
  <div
75
112
  class="${id} ${this.theme}${options?.darkTheme ? `-dark` : ''}"
@@ -77,7 +114,7 @@ const AgGrid = {
77
114
  ? Object.keys(options.style).map((styleKey) => `${styleKey}: ${options.style[styleKey]}; `)
78
115
  : ''}"
79
116
  ></div>
80
- ${usePagination ? `<ag-pagination id="ag-pagination-${id}"></ag-pagination>` : ''}
117
+ ${usePagination ? `<ag-pagination id="ag-pagination-${id}" ${limitOptionsAttr}></ag-pagination>` : ''}
81
118
  `;
82
119
  },
83
120
  RenderStyle: async function (
@@ -159,6 +196,7 @@ const AgGrid = {
159
196
  ${darkTheme
160
197
  ? html`
161
198
  <style>
199
+ .row-new-highlight,
162
200
  .ag-row.row-new-highlight {
163
201
  background-color: #6d68ff !important;
164
202
  transition: background-color 1s ease-out;
@@ -172,6 +210,7 @@ const AgGrid = {
172
210
  </style>
173
211
  `
174
212
  : html`<style>
213
+ .row-new-highlight,
175
214
  .ag-row.row-new-highlight {
176
215
  background-color: #d0eaf8 !important;
177
216
  transition: background-color 1s ease-out;
@@ -947,6 +947,8 @@ const emotionsData = [
947
947
  const userRoleEnum = ['admin', 'moderator', 'user', 'guest'];
948
948
  const commonAdminGuard = (role) => userRoleEnum.indexOf(role) === userRoleEnum.indexOf('admin');
949
949
  const commonModeratorGuard = (role) => userRoleEnum.indexOf(role) <= userRoleEnum.indexOf('moderator');
950
+ const commonUserGuard = (role) => userRoleEnum.indexOf(role) <= userRoleEnum.indexOf('user');
951
+ const commonGuestGuard = (role) => userRoleEnum.indexOf(role) <= userRoleEnum.indexOf('guest');
950
952
 
951
953
  export {
952
954
  s4,
@@ -1003,6 +1005,8 @@ export {
1003
1005
  generateRandomPasswordSelection,
1004
1006
  commonAdminGuard,
1005
1007
  commonModeratorGuard,
1008
+ commonUserGuard,
1009
+ commonGuestGuard,
1006
1010
  isChileanIdentityDocument,
1007
1011
  getCurrentTrace,
1008
1012
  userRoleEnum,
@@ -221,8 +221,11 @@ const borderChar = (px, color, selectors, hover = false) => {
221
221
  (selector) => html`
222
222
  <style>
223
223
  ${selector}${hover ? ':hover' : ''} {
224
- text-shadow: ${px}px -${px}px ${px}px ${color}, -${px}px ${px}px ${px}px ${color},
225
- -${px}px -${px}px ${px}px ${color}, ${px}px ${px}px ${px}px ${color};
224
+ text-shadow:
225
+ ${px}px -${px}px ${px}px ${color},
226
+ -${px}px ${px}px ${px}px ${color},
227
+ -${px}px -${px}px ${px}px ${color},
228
+ ${px}px ${px}px ${px}px ${color};
226
229
  }
227
230
  </style>
228
231
  `,
@@ -239,20 +242,28 @@ const boxShadow = ({ selector }) => html`
239
242
  ? html`
240
243
  <style>
241
244
  ${selector} {
242
- box-shadow: 0 4px 8px 0 rgba(255, 255, 255, 0.1), 0 6px 20px 0 rgba(255, 255, 255, 0.08);
245
+ box-shadow:
246
+ 0 4px 8px 0 rgba(255, 255, 255, 0.1),
247
+ 0 6px 20px 0 rgba(255, 255, 255, 0.08);
243
248
  }
244
249
  ${selector}:hover {
245
- box-shadow: 0 8px 16px 0 rgba(255, 255, 255, 0.15), 0 10px 30px 0 rgba(255, 255, 255, 0.1);
250
+ box-shadow:
251
+ 0 8px 16px 0 rgba(255, 255, 255, 0.15),
252
+ 0 10px 30px 0 rgba(255, 255, 255, 0.1);
246
253
  }
247
254
  </style>
248
255
  `
249
256
  : html`
250
257
  <style>
251
258
  ${selector} {
252
- box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
259
+ box-shadow:
260
+ 0 4px 8px 0 rgba(0, 0, 0, 0.2),
261
+ 0 6px 20px 0 rgba(0, 0, 0, 0.19);
253
262
  }
254
263
  ${selector}:hover {
255
- box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2), 0 10px 30px 0 rgba(0, 0, 0, 0.3);
264
+ box-shadow:
265
+ 0 8px 16px 0 rgba(0, 0, 0, 0.2),
266
+ 0 10px 30px 0 rgba(0, 0, 0, 0.3);
256
267
  }
257
268
  </style>
258
269
  `}
@@ -452,7 +463,9 @@ const typeWriter = async function ({ id, html, seconds, endHideBlink, container
452
463
  overflow: hidden;
453
464
  border-right: 0.15em solid orange;
454
465
  white-space: nowrap;
455
- animation: typing-${id} ${typingAnimationTransitionStyle[1]}, blink-caret-${id} 0.5s step-end infinite;
466
+ animation:
467
+ typing-${id} ${typingAnimationTransitionStyle[1]},
468
+ blink-caret-${id} 0.5s step-end infinite;
456
469
  animation-fill-mode: forwards;
457
470
  width: 0;
458
471
  }
@@ -517,12 +530,20 @@ const dashRange = ({ selector, color }) => {
517
530
  return html`
518
531
  <style>
519
532
  .${selector} {
520
- background: linear-gradient(90deg, ${color} 50%, transparent 50%),
521
- linear-gradient(90deg, ${color} 50%, transparent 50%), linear-gradient(0deg, ${color} 50%, transparent 50%),
522
- linear-gradient(0deg, ${color} 50%, transparent 50%);
533
+ background:
534
+ linear-gradient(90deg, ${color} 50%, transparent 50%), linear-gradient(90deg, ${color} 50%, transparent 50%),
535
+ linear-gradient(0deg, ${color} 50%, transparent 50%), linear-gradient(0deg, ${color} 50%, transparent 50%);
523
536
  background-repeat: repeat-x, repeat-x, repeat-y, repeat-y;
524
- background-size: 16px 4px, 16px 4px, 4px 16px, 4px 16px;
525
- background-position: 0% 0%, 100% 100%, 0% 100%, 100% 0px;
537
+ background-size:
538
+ 16px 4px,
539
+ 16px 4px,
540
+ 4px 16px,
541
+ 4px 16px;
542
+ background-position:
543
+ 0% 0%,
544
+ 100% 100%,
545
+ 0% 100%,
546
+ 100% 0px;
526
547
  border-radius: 5px;
527
548
  padding: 10px;
528
549
  animation: ${selector}_dash_range 5s linear infinite;
@@ -530,7 +551,11 @@ const dashRange = ({ selector, color }) => {
530
551
 
531
552
  @keyframes ${selector}_dash_range {
532
553
  to {
533
- background-position: 100% 0%, 0% 100%, 0% 0%, 100% 100%;
554
+ background-position:
555
+ 100% 0%,
556
+ 0% 100%,
557
+ 0% 0%,
558
+ 100% 100%;
534
559
  }
535
560
  }
536
561
  </style>
@@ -660,8 +685,8 @@ const scrollBarLightRender = () => {
660
685
  ::-` +
661
686
  b +
662
687
  `-scrollbar {
663
- width: 5px;
664
- height: 5px;
688
+ width: 5px;
689
+ height: 5px;
665
690
  }
666
691
 
667
692
  /* Track */
@@ -690,6 +715,10 @@ const scrollBarLightRender = () => {
690
715
  .join('');
691
716
  };
692
717
 
718
+ // adjustHex: supports #RGB #RGBA #RRGGBB #RRGGBBAA
719
+ // preserves alpha channel if present (does not modify it)
720
+ // usage: adjustHex('#24FBFFFF', 0.1)
721
+
693
722
  function adjustHex(hex, factor = 0.1, options = {}) {
694
723
  if (typeof hex !== 'string') throw new TypeError('hex must be a string');
695
724
  if (typeof factor !== 'number') throw new TypeError('factor must be a number');
@@ -701,27 +730,34 @@ function adjustHex(hex, factor = 0.1, options = {}) {
701
730
 
702
731
  const mode = options.mode === 'hsl' ? 'hsl' : 'mix';
703
732
 
704
- // normalize hex
733
+ // normalize hex: accept 3,4,6,8 (with or without #)
705
734
  let h = hex.replace(/^#/, '').trim();
706
- if (!(h.length === 3 || h.length === 6)) throw new Error('Invalid hex format');
707
- if (h.length === 3)
735
+ if (![3, 4, 6, 8].includes(h.length)) {
736
+ throw new Error('Invalid hex format — expected 3, 4, 6 or 8 hex digits');
737
+ }
738
+
739
+ // expand shorthand (#RGB or #RGBA -> #RRGGBB or #RRGGBBAA)
740
+ if (h.length === 3 || h.length === 4) {
708
741
  h = h
709
742
  .split('')
710
743
  .map((c) => c + c)
711
744
  .join('');
745
+ }
746
+
747
+ const hasAlpha = h.length === 8;
712
748
 
713
749
  const r = parseInt(h.slice(0, 2), 16);
714
750
  const g = parseInt(h.slice(2, 4), 16);
715
751
  const b = parseInt(h.slice(4, 6), 16);
752
+ const a = hasAlpha ? parseInt(h.slice(6, 8), 16) : null; // keep alpha as-is if present
716
753
 
717
- const clamp = (v, a = 0, z = 255) => Math.max(a, Math.min(z, v));
754
+ const clamp = (v, a0 = 0, z = 255) => Math.max(a0, Math.min(z, v));
718
755
 
719
- const rgbToHex = (rr, gg, bb) =>
720
- '#' +
721
- [rr, gg, bb]
722
- .map((v) => Math.round(v).toString(16).padStart(2, '0'))
723
- .join('')
724
- .toLowerCase();
756
+ const rgbToHex = (rr, gg, bb, aa = null) => {
757
+ const parts = [rr, gg, bb].map((v) => Math.round(v).toString(16).padStart(2, '0'));
758
+ if (aa !== null) parts.push(Math.round(aa).toString(16).padStart(2, '0'));
759
+ return '#' + parts.join('').toLowerCase();
760
+ };
725
761
 
726
762
  if (mode === 'mix') {
727
763
  // positive: mix toward white (255); negative: mix toward black (0)
@@ -729,11 +765,15 @@ function adjustHex(hex, factor = 0.1, options = {}) {
729
765
  if (factor >= 0) {
730
766
  return clamp(Math.round(c + (255 - c) * factor));
731
767
  } else {
732
- const a = Math.abs(factor);
733
- return clamp(Math.round(c * (1 - a)));
768
+ const aFactor = Math.abs(factor);
769
+ return clamp(Math.round(c * (1 - aFactor)));
734
770
  }
735
771
  };
736
- return rgbToHex(mixChannel(r), mixChannel(g), mixChannel(b));
772
+
773
+ const rr = mixChannel(r);
774
+ const gg = mixChannel(g);
775
+ const bb = mixChannel(b);
776
+ return rgbToHex(rr, gg, bb, a);
737
777
  } else {
738
778
  // HSL mode: convert rgb to hsl, adjust L by factor, convert back
739
779
  const rgbToHsl = (r, g, b) => {
@@ -791,10 +831,16 @@ function adjustHex(hex, factor = 0.1, options = {}) {
791
831
  let newL = ll + factor;
792
832
  newL = Math.max(0, Math.min(1, newL));
793
833
  const { r: r2, g: g2, b: b2 } = hslToRgb(hh, ss, newL);
794
- return rgbToHex(r2, g2, b2);
834
+ return rgbToHex(r2, g2, b2, a);
795
835
  }
796
836
  }
797
837
 
838
+ // Examples (uncomment to test):
839
+ // console.log(adjustHex('#24FBFFFF', 0.1)); // accepts 8-digit input
840
+ // console.log(adjustHex('#24FBFF', 0.1)); // 6-digit
841
+ // console.log(adjustHex('#4bf', -0.2)); // 3-digit
842
+ // console.log(adjustHex('#4bf8', -0.2)); // 4-digit (with alpha)
843
+
798
844
  // Convenience helpers:
799
845
  function lightenHex(hex, percentOr01 = 0.1, options = {}) {
800
846
  return adjustHex(hex, Math.abs(percentOr01), options);
@@ -867,7 +913,7 @@ const scrollBarDarkRender = () => {
867
913
  b +
868
914
  `-scrollbar {
869
915
  width: 8px;
870
- height: 8px;
916
+ height: 8px;
871
917
  /* line-height: 1em; */
872
918
  }
873
919
 
@@ -1011,29 +1057,30 @@ const cssEffect = async (containerSelector, event) => {
1011
1057
  }, 600);
1012
1058
  };
1013
1059
 
1014
- const imageShimmer = () => html`<div
1015
- class="abs center ssr-shimmer-search-box"
1016
- style="${renderCssAttr({
1017
- style: {
1018
- width: '95%',
1019
- height: '95%',
1020
- 'border-radius': '10px',
1021
- overflow: 'hidden',
1022
- },
1023
- })}"
1024
- >
1025
- <div
1026
- class="abs center"
1060
+ const imageShimmer = () =>
1061
+ html`<div
1062
+ class="abs center ssr-shimmer-search-box"
1027
1063
  style="${renderCssAttr({
1028
1064
  style: {
1029
- 'font-size': '70px',
1030
- color: `#bababa`,
1065
+ width: '95%',
1066
+ height: '95%',
1067
+ 'border-radius': '10px',
1068
+ overflow: 'hidden',
1031
1069
  },
1032
1070
  })}"
1033
1071
  >
1034
- <i class="fa-solid fa-photo-film"></i>
1035
- </div>
1036
- </div>`;
1072
+ <div
1073
+ class="abs center"
1074
+ style="${renderCssAttr({
1075
+ style: {
1076
+ 'font-size': '70px',
1077
+ color: `#bababa`,
1078
+ },
1079
+ })}"
1080
+ >
1081
+ <i class="fa-solid fa-photo-film"></i>
1082
+ </div>
1083
+ </div>`;
1037
1084
 
1038
1085
  const renderChessPattern = (patternSize = 20) =>
1039
1086
  `background: repeating-conic-gradient(#808080 0 25%, #0000 0 50%) 50% / ${patternSize}px ${patternSize}px`;
@@ -230,7 +230,6 @@ const CssCommonCore = async () => {
230
230
  .menu-btn-icon {
231
231
  font-size: 20px;
232
232
  width: 40px;
233
- overflow: hidden;
234
233
  text-align: center;
235
234
  }
236
235
  </style>
@@ -57,7 +57,7 @@ const LoadingAnimation = {
57
57
  },
58
58
  spinner: {
59
59
  getId: (id) => `spinner-progress-${id.slice(1)}`,
60
- play: async function (container, spinner) {
60
+ play: async function (container, spinner, options = { append: '', prepend: '' }) {
61
61
  if (!s(container)) return;
62
62
  const id = this.getId(container);
63
63
 
@@ -85,7 +85,7 @@ const LoadingAnimation = {
85
85
  style,
86
86
  })}"
87
87
  >
88
- ${render}
88
+ ${options.prepend ? options.prepend : ''} ${render} ${options.append ? options.append : ''}
89
89
  </div>
90
90
  `,
91
91
  );
@@ -5,16 +5,9 @@ const loggerFactory = (meta, options = { trace: false }) => {
5
5
  const types = ['error', 'warn', 'info', 'debug'];
6
6
  const logger = {
7
7
  log: function (type, args) {
8
- if (location.hostname !== 'localhost' && console.log() !== null) {
9
- console.log = () => null;
10
- console.error = () => null;
11
- console.info = () => null;
12
- console.warn = () => null;
13
- }
8
+ if (!window.renderPayload.dev) return;
14
9
  if (options.trace === true) args.push(getCurrentTrace().split('Logger.js:23')[1]);
15
- return location.hostname === 'localhost'
16
- ? console[type](`[${meta}] ${new Date().toISOString()} ${type}:`, ...args)
17
- : null;
10
+ return console[type](`[${meta}] ${new Date().toISOString()} ${type}:`, ...args);
18
11
  },
19
12
  };
20
13
  types.map(