@underpostnet/cyberia 3.3.76 → 3.4.0

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 (132) hide show
  1. package/.env.example +1 -0
  2. package/.github/workflows/coverall.cyberia.ci.yml +17 -3
  3. package/.github/workflows/cyberia-client.cd.yml +2 -1
  4. package/.github/workflows/cyberia-server.cd.yml +2 -1
  5. package/.github/workflows/docker-image.cyberia-client.ci.yml +1 -1
  6. package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +1 -1
  7. package/.github/workflows/docker-image.cyberia-server.ci.yml +1 -1
  8. package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +1 -1
  9. package/.github/workflows/docker-image.engine-cyberia.ci.yml +1 -1
  10. package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +1 -1
  11. package/.github/workflows/engine-cyberia.ci.yml +7 -1
  12. package/.github/workflows/ghpkg.ci.yml +19 -2
  13. package/.github/workflows/gitlab.ci.yml +8 -6
  14. package/.github/workflows/hardhat.ci.yml +4 -1
  15. package/.github/workflows/npmpkg.ci.yml +9 -5
  16. package/.github/workflows/publish.ci.yml +21 -12
  17. package/.github/workflows/publish.cyberia.ci.yml +25 -14
  18. package/.github/workflows/pwa-microservices-template-page.cd.yml +4 -2
  19. package/CHANGELOG.md +110 -262
  20. package/CLI-HELP.md +29 -2
  21. package/Dockerfile +1 -1
  22. package/Dockerfile.dev +1 -1
  23. package/Dockerfile.test +1 -1
  24. package/README.md +1 -1
  25. package/bin/build.js +67 -14
  26. package/bin/cyberia.js +4 -4
  27. package/bin/index.js +4 -4
  28. package/compose.env +1 -0
  29. package/conf.js +1 -1043
  30. package/deploy/cyberia-client/deploy.sh +13 -11
  31. package/deploy/cyberia-server/deploy.sh +13 -11
  32. package/deploy/dd-cyberia/sync-deploy.sh +7 -28
  33. package/deploy/lib/config.sh +1 -1
  34. package/deploy/lib/github-actions-logging.sh +3 -2
  35. package/deploy/lib/host.sh +1 -1
  36. package/deploy/pwa-microservices-template/deploy.sh +7 -1
  37. package/deployment.yaml +1 -1
  38. package/docker-compose.yml +7 -7
  39. package/docs/coverage/cyberia/api/cyberia-instance/cyberia-fallback-capture.js.html +1591 -0
  40. package/docs/coverage/cyberia/api/cyberia-instance/index.html +116 -0
  41. package/docs/coverage/cyberia/api/cyberia-server-defaults/cyberia-server-defaults.js.html +4138 -0
  42. package/docs/coverage/cyberia/api/cyberia-server-defaults/index.html +116 -0
  43. package/docs/coverage/cyberia/api/object-layer/index.html +116 -0
  44. package/docs/coverage/cyberia/api/object-layer/object-layer.model.js.html +1393 -0
  45. package/docs/coverage/cyberia/base.css +362 -0
  46. package/docs/coverage/cyberia/block-navigation.js +82 -0
  47. package/docs/coverage/cyberia/favicon.png +0 -0
  48. package/docs/coverage/cyberia/index.html +161 -0
  49. package/docs/coverage/cyberia/prettify.css +101 -0
  50. package/docs/coverage/cyberia/prettify.js +937 -0
  51. package/docs/coverage/cyberia/projects/cyberia/atlas-sprite-sheet-generator.js.html +1315 -0
  52. package/docs/coverage/cyberia/projects/cyberia/index.html +176 -0
  53. package/docs/coverage/cyberia/projects/cyberia/instance-backup.js.html +715 -0
  54. package/docs/coverage/cyberia/projects/cyberia/instance-data.js.html +2182 -0
  55. package/docs/coverage/cyberia/projects/cyberia/shape-generator.js.html +3265 -0
  56. package/docs/coverage/cyberia/projects/cyberia/stat-balance.js.html +328 -0
  57. package/docs/coverage/cyberia/sort-arrow-sprite.png +0 -0
  58. package/docs/coverage/cyberia/sorter.js +205 -0
  59. package/hardhat/package-lock.json +6 -6
  60. package/hardhat/package.json +2 -2
  61. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  62. package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +1 -1
  63. package/manifests/deployment/dd-cyberia-development/deployment.yaml +1 -1
  64. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  65. package/package.json +19 -17
  66. package/scripts/test-monitor.sh +1 -1
  67. package/src/api/file/file.router.js +7 -1
  68. package/src/cli/baremetal.js +1 -1
  69. package/src/cli/cluster.js +73 -18
  70. package/src/cli/deploy.js +211 -30
  71. package/src/cli/docker-compose.js +1 -1
  72. package/src/cli/index.js +36 -0
  73. package/src/cli/kubectl.js +4 -3
  74. package/src/cli/release.js +5 -1
  75. package/src/cli/repository.js +14 -11
  76. package/src/cli/run.js +10 -3
  77. package/src/cli/secrets.js +393 -200
  78. package/src/cli/socketsecurity.js +260 -0
  79. package/src/client/components/core/Badge.js +0 -1
  80. package/src/client/components/core/CssCore.js +11 -1
  81. package/src/client/components/core/Modal.js +189 -66
  82. package/src/client/components/core/SearchBox.js +0 -1
  83. package/src/client/components/core/ToolTip.js +15 -0
  84. package/src/client/components/cryptokoyn/AppShellCryptokoyn.js +0 -2
  85. package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +0 -4
  86. package/src/client/components/default/AppShellDefault.js +1 -4
  87. package/src/client/components/itemledger/AppShellItemledger.js +0 -2
  88. package/src/client/components/underpost/AppShellUnderpost.js +12 -0
  89. package/src/client/components/underpost/CssUnderpost.js +15 -2
  90. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +8 -0
  91. package/src/client/public/cyberia-docs/CYBERIA.md +1 -1
  92. package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +54 -56
  93. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  94. package/src/client/public/cyberia-docs/UNDERPOST-PLATFORM.md +30 -29
  95. package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
  96. package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +54 -25
  97. package/src/client/ssr/head/Css.js +12 -10
  98. package/src/client/ssr/views/Cyberia404.js +9 -13
  99. package/src/client/ssr/views/NotFound.js +75 -0
  100. package/src/client-builder/client-bundle.js +2 -2
  101. package/src/index.js +13 -1
  102. package/src/runtime/engine-cyberia/Dockerfile +1 -1
  103. package/src/runtime/engine-cyberia/Dockerfile.dev +1 -1
  104. package/src/runtime/engine-cyberia/Dockerfile.test +1 -1
  105. package/src/runtime/engine-cyberia/compose.env +1 -0
  106. package/src/runtime/engine-cyberia/docker-compose.yml +7 -7
  107. package/src/server/build/catalog.js +1 -0
  108. package/src/server/build/execution.js +19 -5
  109. package/src/server/build/package.js +166 -3
  110. package/src/server/build/testing.js +2 -1
  111. package/src/server/network/underpost-gateway.js +113 -14
  112. package/src/server/runtime/conf.js +57 -7
  113. package/src/server/runtime/config-scope.js +2 -0
  114. package/src/server/runtime/process.js +4 -3
  115. package/src/server/runtime/start.js +11 -2
  116. package/src/server/security/socketsecurity.js +1735 -0
  117. package/test/integration/infra/1-security/config-scope.test.js +3 -0
  118. package/test/integration/infra/1-security/socketsecurity.test.js +1041 -0
  119. package/test/integration/infra/1-security/sops-secret-store.test.js +201 -71
  120. package/test/integration/infra/4-ingress/underpost-ingress.test.js +1 -1
  121. package/test/unit/build-template.test.js +24 -0
  122. package/test/unit/catalog.test.js +13 -2
  123. package/test/unit/conf-resolution.test.js +41 -7
  124. package/test/unit/cyberia/instance-data.test.js +314 -0
  125. package/test/unit/deploy-legacy-gateway-sweep.test.js +63 -0
  126. package/test/unit/deploy-log-table.test.js +38 -0
  127. package/test/unit/execution-profiles.test.js +1 -0
  128. package/test/unit/gateway-pod-fetch.test.js +95 -0
  129. package/test/unit/letsencrypt-issuer.test.js +101 -0
  130. package/test/unit/package.test.js +118 -0
  131. package/test/unit/release-bump.test.js +3 -1
  132. package/test/unit/start-options.test.js +12 -0
@@ -129,6 +129,9 @@ class Modal {
129
129
  /** @type {Object.<string, ModalDataEntry>} */
130
130
  static Data = {};
131
131
 
132
+ /** @type {Function|null} Recomputes visibility of the search/home toggle button; set once the slide-menu modal renders it. */
133
+ static updateBarCustomVisibility = null;
134
+
132
135
  /**
133
136
  * Bottom offset of the slide-menu area for the given options.
134
137
  * @param {ModalRenderOptions} options
@@ -228,6 +231,10 @@ class Modal {
228
231
  const originHeightTopBar = 50;
229
232
  options.heightBottomBar = 0;
230
233
  options.heightTopBar = 100;
234
+ // A view sits under its slide menu's bars, so it follows that menu's bar mode unless it
235
+ // names one — otherwise a 'top-bottom-bar' shell opens every view 100px down instead of 50px.
236
+ if (options && !options.barMode && options.slideMenu && Modal.Data[options.slideMenu]?.options?.barMode)
237
+ options.barMode = Modal.Data[options.slideMenu].options.barMode;
231
238
  if (options && options.barMode && options.barMode === 'top-bottom-bar') {
232
239
  options.heightTopBar = 50;
233
240
  options.heightBottomBar = 50;
@@ -297,6 +304,9 @@ class Modal {
297
304
  route: options.route,
298
305
  RouterInstance: options.RouterInstance,
299
306
  });
307
+ // Path just changed away from (or back to) home — recompute the search/home
308
+ // toggle's visibility instead of leaving it stuck on its last computed state.
309
+ setTimeout(() => Modal.updateBarCustomVisibility?.());
300
310
  }
301
311
 
302
312
  break;
@@ -319,10 +329,15 @@ class Modal {
319
329
  padding: 5px;
320
330
  }
321
331
  .default-slide-menu-top-bar-fix-title-container-text {
322
- font-size: 26px;
323
- top: 8px;
324
332
  color: ${darkTheme ? '#ffffff' : '#000000'};
325
333
  }
334
+ /* Themes size and offset the title for the 100px banner with !important;
335
+ the 50px one has to win over them or the title spills into the menu. */
336
+ .slide-menu-top-bar-fix .default-slide-menu-top-bar-fix-title-container-text {
337
+ font-size: 26px !important;
338
+ top: 8px !important;
339
+ left: 0px !important;
340
+ }
326
341
  </style>`;
327
342
  } else {
328
343
  style = html`<style>
@@ -415,17 +430,41 @@ class Modal {
415
430
  },
416
431
  { key: `slide-menu-${idModal}` },
417
432
  );
418
- // The fixed hamburger only opens the menu: it shows while the top bar is collapsed
419
- // and the menu is closed. An open menu (50px or full) carries its own controls and
420
- // already pushes the views aside, so neither the button nor the title padding apply.
433
+ // The fixed hamburger stands in for the top bar's while it is collapsed, opening and
434
+ // closing the menu alike. Its 50px corner is kept clear two ways: the menu content is
435
+ // padded down while it shows, and a view title is padded right only while the menu
436
+ // is closed — an open menu (50px or full) already pushes the views aside.
421
437
  const syncFixedMenuButton = () => {
422
438
  const fixed = s(`.main-btn-menu-top-fixed-container`);
423
439
  if (!fixed) return;
424
440
  const collapsed = s(`.main-body-btn-ui-close`) && s(`.main-body-btn-ui-close`).classList.contains('hide');
425
- const active = collapsed && Modal.Data[idModal][options.mode].width === 0;
426
- fixed.classList[active ? 'remove' : 'add']('hide');
427
- s('body').classList[active ? 'add' : 'remove']('main-btn-menu-top-fixed-active');
441
+ const menuClosed = Modal.Data[idModal][options.mode].width === 0;
442
+ fixed.classList[collapsed ? 'remove' : 'add']('hide');
443
+ s('body').classList[collapsed ? 'add' : 'remove']('main-btn-menu-top-fixed-active');
444
+ s('body').classList[collapsed && menuClosed ? 'add' : 'remove']('main-btn-menu-top-fixed-title-offset');
445
+ };
446
+ // Hides the search/home toggle only while it's showing the "close" (home) icon
447
+ // AND the body is already scrolled to top — nothing left for it to do there.
448
+ // Reactive: called on every scroll tick and state change, so it restores to its
449
+ // normal visible state the moment either condition stops holding.
450
+ const updateBarCustomVisibility = () => {
451
+ const barCustom = s(`.main-body-btn-bar-custom`);
452
+ if (!barCustom || !options?.slideMenuTopBarBannerFix) return;
453
+ const mainBody = s(`.main-body`);
454
+ const atTop = !!mainBody && mainBody.scrollTop <= 0;
455
+ const bannerFix = s(`.slide-menu-top-bar-fix`);
456
+ const bannerVisible = !!bannerFix && parseInt(bannerFix.style.top, 10) >= 0;
457
+ const isHomePath = location.pathname === getProxyPath();
458
+ // Never hide while the open (magnifying-glass) icon is showing — that's the
459
+ // idle "click to search" state, not the "close/home" state this hide targets.
460
+ const openIconHidden =
461
+ !!s(`.main-body-btn-ui-bar-custom-open`) &&
462
+ s(`.main-body-btn-ui-bar-custom-open`).classList.contains('hide');
463
+ barCustom.classList[bannerVisible && isHomePath && atTop && openIconHidden ? 'add' : 'remove']('hide');
428
464
  };
465
+ // Exposed statically so other code paths (e.g. opening a view/route) can force a
466
+ // recompute — otherwise the hidden state goes stale once the path stops being home.
467
+ Modal.updateBarCustomVisibility = updateBarCustomVisibility;
429
468
  barConfig.buttons.menu.onClick = () => {
430
469
  Modal.Data[idModal][options.mode].width = slideMenuWidth;
431
470
  s(`.btn-menu-${idModal}`).classList.add('hide');
@@ -450,6 +489,10 @@ class Modal {
450
489
  }
451
490
  syncFixedMenuButton();
452
491
  Responsive.triggerChanged(`slide-menu-${idModal}`);
492
+ if (options.onExtendMenu) options.onExtendMenu();
493
+ Object.keys(this.Data[idModal].onExtendMenuListener).map((keyListener) =>
494
+ this.Data[idModal].onExtendMenuListener[keyListener](),
495
+ );
453
496
  };
454
497
  barConfig.buttons.close.onClick = () => {
455
498
  Modal.Data[idModal][options.mode].width = 0;
@@ -474,6 +517,10 @@ class Modal {
474
517
  }
475
518
  syncFixedMenuButton();
476
519
  Responsive.triggerChanged(`slide-menu-${idModal}`);
520
+ if (options.onCollapseMenu) options.onCollapseMenu();
521
+ Object.keys(this.Data[idModal].onCollapseMenuListener).map((keyListener) =>
522
+ this.Data[idModal].onCollapseMenuListener[keyListener](),
523
+ );
477
524
  };
478
525
  transition += `, width 0.3s`;
479
526
 
@@ -530,14 +577,11 @@ class Modal {
530
577
  Modal.actionBtnCenter();
531
578
  };
532
579
 
533
- // Fixed top-left hamburger, shown only while the top bar is collapsed
534
- // (main-body-btn-ui-close hidden). Not needed for 'top-bottom-bar' which
535
- // keeps its own center action button in the bottom bar.
536
- if (
537
- idModal === 'modal-menu' &&
538
- options.mode !== 'slide-menu-right' &&
539
- options.barMode !== 'top-bottom-bar'
540
- ) {
580
+ // Fixed top-left hamburger, shown only while the bars are collapsed
581
+ // (main-body-btn-ui-close hidden): it stands in for the left menu hamburger the
582
+ // collapse hid the top bar's, or the bottom bar's under 'top-bottom-bar'. A right
583
+ // menu keeps its floating .main-body-btn-menu beside the menu edge instead.
584
+ if (idModal === 'modal-menu' && options.mode !== 'slide-menu-right') {
541
585
  append(
542
586
  'body',
543
587
  html`
@@ -548,17 +592,24 @@ class Modal {
548
592
  ${await BtnIcon.instance({
549
593
  style: `height: 100%`,
550
594
  class: `in fll main-btn-menu-top action-bar-box action-btn-center-top-fixed`,
551
- label: html`<div class="abs center">${barConfig.buttons.menu.label}</div>`,
595
+ label: html`<div class="abs center">
596
+ <span class="btn-bar-center-icon-close hide">${barConfig.buttons.close.label}</span>
597
+ <span class="btn-bar-center-icon-menu">${barConfig.buttons.menu.label}</span>
598
+ </div>`,
552
599
  })}
553
600
  </div>
554
601
  <style>
602
+ /* The menu content starts below the fixed hamburger while it shows. */
603
+ body.main-btn-menu-top-fixed-active .html-${idModal} {
604
+ padding-top: ${originHeightTopBar}px;
605
+ }
555
606
  /* View modals sit at top: 0 while the top bar is collapsed; keep their
556
607
  title clear of the fixed hamburger. The padding eases at the same pace
557
608
  the views slide so the title moves once, not a jump and a slide back. */
558
609
  .title-main-modal {
559
610
  transition: padding-left 0.3s;
560
611
  }
561
- body.main-btn-menu-top-fixed-active .title-main-modal {
612
+ body.main-btn-menu-top-fixed-title-offset .title-main-modal {
562
613
  padding-left: ${originHeightTopBar}px;
563
614
  }
564
615
  </style>
@@ -578,19 +629,38 @@ class Modal {
578
629
  s(`.main-body-btn-ui-bar-custom-open`).classList.remove('hide');
579
630
  s(`.main-body-btn-ui-bar-custom-close`).classList.add('hide');
580
631
  s(`.slide-menu-top-bar-fix`).style.top = '0px';
632
+
633
+ setTimeout(async () => {
634
+ setTimeout(() => {
635
+ s(`.action-btn-home`).click();
636
+ });
637
+ const mainBody = s(`.main-body`);
638
+ if (mainBody && mainBody.scrollTop > 0) {
639
+ mainBody.scrollTo({ top: 0, behavior: 'smooth' });
640
+ }
641
+ updateBarCustomVisibility();
642
+ });
581
643
  } else {
582
644
  s(`.main-body-btn-ui-bar-custom-open`).classList.add('hide');
583
645
  s(`.main-body-btn-ui-bar-custom-close`).classList.remove('hide');
584
646
  s(`.slide-menu-top-bar-fix`).style.top = '-100px';
585
647
  s(`.top-bar-search-box-container`).click();
586
648
  }
649
+ updateBarCustomVisibility();
587
650
  if (Modal.mobileModal()) {
588
651
  btnCloseEvent();
589
652
  }
590
653
  };
591
654
 
592
- let _heightTopBar, _heightBottomBar, _topMenu;
655
+ let _heightTopBar, _heightBottomBar, _topMenu, _barsAnimationTimeout;
656
+ // The layout offsets below are set inline, so they only ease while this class is on.
657
+ const animateBarsToggle = () => {
658
+ s('body').classList.add('ui-bars-animating');
659
+ clearTimeout(_barsAnimationTimeout);
660
+ _barsAnimationTimeout = setTimeout(() => s('body').classList.remove('ui-bars-animating'), 300);
661
+ };
593
662
  s(`.main-body-btn-ui`).onclick = () => {
663
+ animateBarsToggle();
594
664
  if (s(`.main-body-btn-ui-open`).classList.contains('hide')) {
595
665
  s(`.main-body-btn-ui-open`).classList.remove('hide');
596
666
  s(`.main-body-btn-ui-close`).classList.add('hide');
@@ -599,8 +669,7 @@ class Modal {
599
669
  _topMenu = newInstance(s(`.modal-menu`).style.top);
600
670
  options.heightTopBar = 0;
601
671
  options.heightBottomBar = 0;
602
- s(`.slide-menu-top-bar`).classList.add('hide');
603
- s(`.bottom-bar`).classList.add('hide');
672
+ s('body').classList.add('ui-bars-collapsed');
604
673
  syncFixedMenuButton();
605
674
  s(`.modal-menu`).style.top = '0px';
606
675
  s(`.main-body-btn-container`).style.top = '50px';
@@ -615,8 +684,7 @@ class Modal {
615
684
  options.heightBottomBar = _heightBottomBar;
616
685
  s(`.modal-menu`).style.top = _topMenu;
617
686
  s(`.main-body-btn-container`).style.top = `${options.heightTopBar + 50}px`;
618
- s(`.slide-menu-top-bar`).classList.remove('hide');
619
- s(`.bottom-bar`).classList.remove('hide');
687
+ s('body').classList.remove('ui-bars-collapsed');
620
688
  syncFixedMenuButton();
621
689
  s(`.main-body`).style.top = `${options.heightTopBar}px`;
622
690
  s(`.main-body`).style.height = `${windowGetH() - options.heightTopBar}px`;
@@ -640,13 +708,62 @@ class Modal {
640
708
  );
641
709
  }
642
710
  };
711
+ updateBarCustomVisibility();
643
712
  Modal.setTopBannerLink();
644
713
  });
645
714
 
646
715
  const inputSearchBoxId = `top-bar-search-box`;
647
716
  append(
648
717
  'body',
649
- html` <div class="fix modal slide-menu-top-bar">
718
+ html`<style>
719
+ /* Collapsing slides the bars off-screen instead of removing them; they turn
720
+ invisible only once the slide ends, so no hidden control stays focusable. */
721
+ .slide-menu-top-bar,
722
+ .bottom-bar {
723
+ transition:
724
+ transform 0.3s ease,
725
+ opacity 0.3s ease,
726
+ visibility 0s linear 0s !important;
727
+ }
728
+ body.ui-bars-collapsed .slide-menu-top-bar {
729
+ transform: translateY(-100%);
730
+ }
731
+ body.ui-bars-collapsed .bottom-bar {
732
+ transform: translateY(100%) !important;
733
+ }
734
+ /* A bottom bar rendered inside the top bar leaves with it. */
735
+ body.ui-bars-collapsed .slide-menu-top-bar .bottom-bar {
736
+ transform: none !important;
737
+ }
738
+ body.ui-bars-collapsed .slide-menu-top-bar,
739
+ body.ui-bars-collapsed .bottom-bar {
740
+ visibility: hidden;
741
+ pointer-events: none;
742
+ transition:
743
+ transform 0.3s ease,
744
+ opacity 0.3s ease,
745
+ visibility 0s linear 0.3s !important;
746
+ }
747
+ body.ui-bars-animating .main-body,
748
+ body.ui-bars-animating .main-body-btn-container {
749
+ transition:
750
+ top 0.3s ease,
751
+ left 0.3s ease,
752
+ height 0.3s ease,
753
+ opacity 0.3s ease !important;
754
+ }
755
+ @media (prefers-reduced-motion: reduce) {
756
+ .slide-menu-top-bar,
757
+ .bottom-bar,
758
+ body.ui-bars-collapsed .slide-menu-top-bar,
759
+ body.ui-bars-collapsed .bottom-bar,
760
+ body.ui-bars-animating .main-body,
761
+ body.ui-bars-animating .main-body-btn-container {
762
+ transition: none !important;
763
+ }
764
+ }
765
+ </style>
766
+ <div class="fix modal slide-menu-top-bar">
650
767
  <div
651
768
  class="fl top-bar ${options.barClass ? options.barClass : ''}"
652
769
  style="height: ${originHeightTopBar}px;"
@@ -739,24 +856,28 @@ class Modal {
739
856
  </div>`
740
857
  : ''}
741
858
  ${idModal === 'modal-menu' && options.mode !== 'slide-menu-right'
742
- ? html`<div
743
- class="abs main-btn-menu-top-container"
744
- style="bottom: 0px; left: 0px; z-index: 10; height: ${originHeightTopBar}px; width: ${originHeightTopBar}px"
745
- >
746
- ${await BtnIcon.instance({
747
- style: `height: 100%`,
748
- class: `in fll main-btn-menu-top action-bar-box action-btn-center-top`,
749
- label: html`<div class="abs center">
750
- <i class="far fa-square btn-bar-center-icon-square hide"></i>
751
- <span class="btn-bar-center-icon-close hide">${barConfig.buttons.close.label}</span>
752
- <span class="btn-bar-center-icon-menu">${barConfig.buttons.menu.label}</span>
753
- </div>`,
754
- })}
755
- </div>
859
+ ? html`${options.barMode === 'top-bottom-bar'
860
+ ? ''
861
+ : html`<div
862
+ class="abs main-btn-menu-top-container"
863
+ style="bottom: 0px; left: 0px; z-index: 10; height: ${originHeightTopBar}px; width: ${originHeightTopBar}px"
864
+ >
865
+ ${await BtnIcon.instance({
866
+ style: `height: 100%`,
867
+ class: `in fll main-btn-menu-top action-bar-box action-btn-center-top`,
868
+ label: html`<div class="abs center">
869
+ <i class="far fa-square btn-bar-center-icon-square hide"></i>
870
+ <span class="btn-bar-center-icon-close hide">${barConfig.buttons.close.label}</span>
871
+ <span class="btn-bar-center-icon-menu">${barConfig.buttons.menu.label}</span>
872
+ </div>`,
873
+ })}
874
+ </div>`}
756
875
 
757
876
  <style>
877
+ /* Under 'top-bottom-bar' the left menu hamburger is the bottom bar's, so the
878
+ banner has no top bar button to clear. */
758
879
  .a-link-top-banner {
759
- padding-left: 35px;
880
+ padding-left: ${options.barMode === 'top-bottom-bar' ? 0 : 35}px;
760
881
  }
761
882
  .main-body-btn-bar-custom {
762
883
  top: 50px !important;
@@ -780,7 +901,11 @@ class Modal {
780
901
  }
781
902
  s(`.main-btn-sign-up`).click();
782
903
  });
783
- if (idModal === 'modal-menu' && options.mode !== 'slide-menu-right') {
904
+ if (
905
+ idModal === 'modal-menu' &&
906
+ options.mode !== 'slide-menu-right' &&
907
+ options.barMode !== 'top-bottom-bar'
908
+ ) {
784
909
  EventsUI.onClick(`.action-btn-center-top`, (e) => {
785
910
  e.preventDefault();
786
911
  Modal.actionBtnCenter();
@@ -1151,6 +1276,7 @@ class Modal {
1151
1276
  } catch (e) {}
1152
1277
  }
1153
1278
  Modal.removeModal(searchBoxHistoryId);
1279
+ updateBarCustomVisibility();
1154
1280
  };
1155
1281
  s('.top-bar-search-box').onblur = () => {
1156
1282
  hoverFocusCtl.checkDismiss();
@@ -1376,10 +1502,15 @@ class Modal {
1376
1502
  >
1377
1503
  ${await BtnIcon.instance({
1378
1504
  style: `height: 100%`,
1379
- class: `in fl${
1380
- options.mode === 'slide-menu-right' ? 'r' : 'l'
1381
- } main-btn-menu action-bar-box action-btn-center ${
1382
- options?.disableTools?.includes('center') ? 'hide' : ''
1505
+ // The left menu hamburger under 'top-bottom-bar'. Otherwise a left menu is
1506
+ // toggled from the top bar and a right menu from the floating
1507
+ // .main-body-btn-menu, so a copy here would only duplicate one of them.
1508
+ class: `in fl${options.mode === 'slide-menu-right' ? 'r' : 'l'} main-btn-menu action-bar-box action-btn-center ${
1509
+ options?.disableTools?.includes('center') ||
1510
+ options.mode === 'slide-menu-right' ||
1511
+ options.barMode !== 'top-bottom-bar'
1512
+ ? 'hide'
1513
+ : ''
1383
1514
  }`,
1384
1515
  label: html`
1385
1516
  <div class="${contentIconClass}">
@@ -1653,6 +1784,8 @@ class Modal {
1653
1784
  removeEvent();
1654
1785
  }
1655
1786
  });
1787
+ Scroll.setEvent('.main-body', () => updateBarCustomVisibility());
1788
+ updateBarCustomVisibility();
1656
1789
  setTimeout(window.onresize);
1657
1790
  setRouterReady();
1658
1791
  });
@@ -2884,28 +3017,18 @@ const renderViewTitle = (
2884
3017
  </div>`;
2885
3018
  };
2886
3019
 
2887
- const buildBadgeToolTipMenuOption = (id, sideKey = 'left') => {
2888
- const option = {
2889
- id: `tooltip-content-main-btn-${id}`,
2890
- text: `${Translate.instance(`${id}`)}`,
2891
- classList: 'tooltip-menu',
2892
- style: { top: `-40px` },
2893
- };
2894
- switch (sideKey) {
2895
- case 'left':
2896
- option.style.left = '60px';
2897
-
2898
- break;
2899
-
2900
- case 'right':
2901
- option.style.right = '60px';
2902
- break;
2903
-
2904
- default:
2905
- break;
2906
- }
2907
- return option;
2908
- };
3020
+ /**
3021
+ * Tooltip badge for a collapsed menu button. ToolTip places it beside the button on the side
3022
+ * the menu leaves free, read from the menu mode when it shows — so a shell never pins an
3023
+ * offset that only holds for one of 'slide-menu' or 'slide-menu-right'.
3024
+ * @param {string} id - Translation key and tooltip id suffix.
3025
+ * @param {'left'|'right'} [sideKey] - Forces the side the menu sits on; omit to follow the mode.
3026
+ */
3027
+ const buildBadgeToolTipMenuOption = (id, sideKey) => ({
3028
+ id: `tooltip-content-main-btn-${id}`,
3029
+ text: `${Translate.instance(`${id}`)}`,
3030
+ classList: `tooltip-menu${sideKey ? ` tooltip-menu-side-${sideKey}` : ''}`,
3031
+ });
2909
3032
 
2910
3033
  const isSubMenuOpen = (subMenuId) => {
2911
3034
  return s(`.down-arrow-submenu-${subMenuId}`) && s(`.down-arrow-submenu-${subMenuId}`).style.rotate === '180deg';
@@ -832,7 +832,6 @@ class SearchBox {
832
832
 
833
833
  .search-result-route {
834
834
  padding: 10px 12px;
835
- margin: 2px;
836
835
  text-align: left;
837
836
  min-height: 40px;
838
837
  }
@@ -58,6 +58,21 @@ class ToolTip {
58
58
  const tooltipRect = tooltipEl.getBoundingClientRect();
59
59
  let top = containerRect.bottom + window.scrollY + 5;
60
60
  let left = containerRect.left + window.scrollX + containerRect.width / 2 - tooltipRect.width / 2;
61
+ if (options.useMenuBtn) {
62
+ // Beside the collapsed menu button, on the side the menu leaves free: a right-hand menu
63
+ // sits on the window edge, so its tooltips open leftwards. A shell may pin the side.
64
+ const pinned = tooltipEl.querySelector('.tooltip-menu-side-right')
65
+ ? 'right'
66
+ : tooltipEl.querySelector('.tooltip-menu-side-left')
67
+ ? 'left'
68
+ : null;
69
+ const menuSide = pinned || (Modal.Data['modal-menu'].options.mode === 'slide-menu-right' ? 'right' : 'left');
70
+ top = containerRect.top + window.scrollY + (containerRect.height - tooltipRect.height) / 2;
71
+ left =
72
+ menuSide === 'right'
73
+ ? containerRect.left + window.scrollX - tooltipRect.width - 10
74
+ : containerRect.right + window.scrollX + 10;
75
+ }
61
76
  // Adjust if it goes off-screen
62
77
  if (left < 0) left = 5;
63
78
  if (left + tooltipRect.width > window.innerWidth) {
@@ -26,8 +26,6 @@ class AppShellCryptokoyn {
26
26
  const RouterInstance = RouterCryptokoyn.instance();
27
27
 
28
28
  const { barConfig } = await Themes[Css.currentTheme]();
29
-
30
- const badgeNotificationMenuStyle = { top: '-33px', left: '24px' };
31
29
  const barMode = undefined; // 'top-bottom-bar';
32
30
  await Modal.instance({
33
31
  id: 'modal-menu',
@@ -56,10 +56,6 @@ class RouterCyberiaPortal {
56
56
  title: 'cyberia-entity-engine',
57
57
  render: () => s(`.main-btn-cyberia-entity-engine`).click(),
58
58
  },
59
- '/fallback-world-engine': {
60
- title: 'fallback-world-engine',
61
- render: () => s(`.main-btn-fallback-world-engine`).click(),
62
- },
63
59
  '/instance-selection': {
64
60
  title: 'instance-selection',
65
61
  render: () => s(`.main-btn-instance-selection`).click(),
@@ -31,8 +31,6 @@ class AppShellDefault {
31
31
  const RouterInstance = RouterDefault.instance();
32
32
 
33
33
  const { barConfig } = await Themes[Css.currentTheme]();
34
-
35
- const badgeNotificationMenuStyle = { top: '-33px', left: '24px' };
36
34
  const barMode = undefined; // 'top-bottom-bar';
37
35
  await Modal.instance({
38
36
  id: 'modal-menu',
@@ -197,8 +195,7 @@ class AppShellDefault {
197
195
  ${await Badge.instance({
198
196
  id: 'main-btn-chat',
199
197
  type: 'circle-red',
200
- style: badgeNotificationMenuStyle,
201
- classList: 'hide',
198
+ classList: 'hide badge-notification-menu',
202
199
  })}`,
203
200
  attrs: `data-id="chat"`,
204
201
  tabHref: `${getProxyPath()}chat`,
@@ -27,8 +27,6 @@ class AppShellItemledger {
27
27
  const RouterInstance = RouterItemledger.instance();
28
28
 
29
29
  const { barConfig } = await Themes[Css.currentTheme]();
30
-
31
- const badgeNotificationMenuStyle = { top: '-33px', left: '24px' };
32
30
  const barMode = undefined; // 'top-bottom-bar';
33
31
  await Modal.instance({
34
32
  id: 'modal-menu',
@@ -227,6 +227,8 @@ class AppShellUnderpost {
227
227
  titleRender: () => {
228
228
  return '';
229
229
  },
230
+ // mode: 'slide-menu-right',
231
+ barMode: 'top-bottom-bar',
230
232
  mode: 'slide-menu',
231
233
  RouterInstance,
232
234
  searchCustomImgClass: 'underpost-menu-icon',
@@ -305,6 +307,16 @@ class AppShellUnderpost {
305
307
  return panelFormInstance;
306
308
  },
307
309
  });
310
+ for (const eventFn of ['onCollapseMenuListener', 'onExtendMenuListener', 'onExpandUiListener', 'onCloseListener']) {
311
+ Modal.Data['modal-menu'][eventFn]['underpost-panel-form-padding'] = (arg) => {
312
+ if (s(`.underpost-panel-form-container`))
313
+ s(`.underpost-panel-form-container`).style.paddingLeft =
314
+ !s('.main-body-btn-ui-open').classList.contains('hide') &&
315
+ s(`.btn-bar-center-icon-close`).classList.contains('hide')
316
+ ? '50px'
317
+ : '0px';
318
+ };
319
+ }
308
320
 
309
321
  ThemeEvents['underpost-main-theme-event'] = () => {
310
322
  const srcLogo = darkTheme
@@ -49,6 +49,9 @@ const CssCommonUnderpost = async () => {
49
49
  font-family: 'retro-font-cta';
50
50
  src: URL('${getProxyPath()}assets/fonts/PressStart2P-Regular.ttf') format('truetype');
51
51
  }
52
+ .default-slide-menu-top-bar-fix-title-container {
53
+ top: 10px;
54
+ }
52
55
 
53
56
  /* Landing Page & Object Viewer Styles */
54
57
  .landing-container {
@@ -176,6 +179,15 @@ const CssCommonUnderpost = async () => {
176
179
  .underpost-panel-subtitle {
177
180
  top: 3px !important;
178
181
  }
182
+ /* Clears the fixed hamburger at the pace the bars slide. */
183
+ .underpost-panel-form-container {
184
+ transition: padding-left 0.3s ease;
185
+ }
186
+ @media (prefers-reduced-motion: reduce) {
187
+ .underpost-panel-form-container {
188
+ transition: none;
189
+ }
190
+ }
179
191
  .underpost-panel-cell,
180
192
  .underpost-panel-cell p,
181
193
  .underpost-panel-cell span,
@@ -236,15 +248,15 @@ class CssUnderpostDark {
236
248
  button:hover,
237
249
  .a-btn:hover {
238
250
  background: #212020;
251
+ color: #ffcc00 !important;
239
252
  }
240
253
  .action-bar-box {
241
254
  color: white;
242
255
  }
243
256
  .default-slide-menu-top-bar-fix-title-container-text {
244
- color: black !important;
257
+ color: white !important;
245
258
  }
246
259
  </style>
247
- ${borderChar(2, `#f70808`, ['.default-slide-menu-top-bar-fix-title-container-text'])}
248
260
  `
249
261
  );
250
262
  };
@@ -262,6 +274,7 @@ class CssUnderpostLight {
262
274
  button:hover,
263
275
  .a-btn:hover {
264
276
  background: #d8d8d8;
277
+ color: #ffcc00 !important;
265
278
  }
266
279
 
267
280
  .action-bar-box {
@@ -340,6 +340,14 @@ wasm-driver.py --data-server-url=<origin>
340
340
  required: the client asserts and stops if it is absent. There is no environment
341
341
  fallback, no config file and no runtime override.
342
342
 
343
+ `wasm-driver.py` appends any other flag to the client argv. `fetch_init()` reads
344
+ two stream diagnostics:
345
+
346
+ | Flag | Effect |
347
+ | ------------------------------------- | ------------------------------------------------------------------------------------ |
348
+ | `--stream-disable=<mode>[,<mode>...]` | Turns off `audio`, `audio-network`, `audio-runtime`, `atlas` or `dynamic` streaming. |
349
+ | `--stream-profile` | Records stream events in `Module.streamTrace` (30,000-entry ring, read in DevTools). |
350
+
343
351
  ### Constants (`src/config.h`)
344
352
 
345
353
  | Constant | Default | Description |
@@ -20,7 +20,7 @@ _Stackable Rendering Layers as a Unified Tokenized Reality_
20
20
  <!-- CI/CD and health -->
21
21
  <div align="center">
22
22
 
23
- [![Node.js CI](https://img.shields.io/github/actions/workflow/status/underpostnet/engine-cyberia/docker-image.engine-cyberia.ci.yml?branch=main&label=Node.js%20CI)](https://github.com/underpostnet/engine-cyberia/actions/workflows/docker-image.engine-cyberia.ci.yml) [![Test](https://img.shields.io/github/actions/workflow/status/underpostnet/engine-cyberia/coverall.cyberia.ci.yml?branch=main&label=Test)](https://github.com/underpostnet/engine-cyberia/actions/workflows/coverall.cyberia.ci.yml) [![Coverage Status](https://coveralls.io/repos/github/underpostnet/engine-cyberia/badge.svg?branch=main)](https://coveralls.io/github/underpostnet/engine-cyberia?branch=main) [![Socket Badge](https://socket.dev/api/badge/npm/package/cyberia/3.3.76)](https://socket.dev/npm/package/cyberia/overview/3.3.76)
23
+ [![Node.js CI](https://img.shields.io/github/actions/workflow/status/underpostnet/engine-cyberia/docker-image.engine-cyberia.ci.yml?branch=main&label=Node.js%20CI)](https://github.com/underpostnet/engine-cyberia/actions/workflows/docker-image.engine-cyberia.ci.yml) [![Test](https://img.shields.io/github/actions/workflow/status/underpostnet/engine-cyberia/coverall.cyberia.ci.yml?branch=main&label=Test)](https://github.com/underpostnet/engine-cyberia/actions/workflows/coverall.cyberia.ci.yml) [![Coverage Status](https://coveralls.io/repos/github/underpostnet/engine-cyberia/badge.svg?branch=main)](https://coveralls.io/github/underpostnet/engine-cyberia?branch=main) [![Socket Badge](https://socket.dev/api/badge/npm/package/cyberia/3.4.0)](https://socket.dev/npm/package/cyberia/overview/3.4.0)
24
24
 
25
25
  </div>
26
26