@omega.js/desktop 0.51.0 → 0.53.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.
- package/README.md +18 -13
- package/dist/assets/css/core/_initialize.scss +1 -1
- package/dist/assets/js/core/app-shell.js +14 -15
- package/dist/assets/themes/_template/_theme.js +6 -6
- package/dist/assets/themes/base/_includes/global/sections/account.html +4 -4
- package/dist/assets/themes/base/_includes/global/sections/app-sidebar.html +5 -5
- package/dist/assets/themes/base/_layouts/frontend/pages/account/index.html +3 -3
- package/dist/assets/themes/base/_layouts/frontend/pages/alternatives/index.html +1 -1
- package/dist/assets/themes/base/_layouts/frontend/pages/blog/tags/tag.html +1 -1
- package/dist/assets/themes/base/_layouts/frontend/pages/payment/confirmation.html +1 -1
- package/dist/assets/themes/base/_sections/marketing/newsletter-cta/section.js +2 -3
- package/dist/assets/themes/base/_sections/verts/unit/section.html +1 -1
- package/dist/assets/themes/base/_sections/verts/unit/section.js +3 -4
- package/dist/assets/themes/base/_theme.js +4 -3
- package/dist/assets/themes/bootstrap/_theme.js +2 -2
- package/dist/assets/themes/bootstrap/overrides/_links.scss +1 -1
- package/dist/assets/themes/classy/_theme.js +8 -6
- package/dist/assets/themes/classy/css/marketing/_sections.scss +1 -2
- package/dist/assets/themes/classy/js/hero-demo-form.js +3 -2
- package/dist/assets/themes/neobrutalism/_theme.js +5 -5
- package/dist/assets/themes/neobrutalism/js/pages/test/libraries/layers/index.js +1 -1
- package/dist/assets/themes/newsflash/_theme.js +6 -6
- package/dist/assets/themes/newsflash/js/pages/test/libraries/layers/index.js +1 -1
- package/dist/build.js +87 -111
- package/dist/cli.js +1 -1
- package/dist/commands/build.js +2 -2
- package/dist/commands/cdp/capture.js +2 -2
- package/dist/commands/cdp/quit.js +2 -2
- package/dist/commands/cdp/relaunch.js +2 -2
- package/dist/commands/cdp/theme.js +1 -1
- package/dist/commands/clean.js +2 -2
- package/dist/commands/deploy.js +4 -4
- package/dist/commands/finalize-release.js +4 -4
- package/dist/commands/install.js +3 -3
- package/dist/commands/launch.js +2 -2
- package/dist/commands/lib/deploy-precheck.js +3 -3
- package/dist/commands/lib/ensure-target.js +6 -6
- package/dist/commands/package.js +2 -2
- package/dist/commands/publish.js +2 -2
- package/dist/commands/release.js +3 -3
- package/dist/commands/runner.js +145 -48
- package/dist/commands/sign-windows.js +5 -5
- package/dist/commands/test.js +4 -4
- package/dist/commands/update.js +2 -2
- package/dist/commands/validate-certs.js +4 -4
- package/dist/commands/version.js +3 -3
- package/dist/defaults/.github/workflows/build.yml +2 -0
- package/dist/defaults/AGENTS.md +17 -8
- package/dist/defaults/config/omega.json5 +7 -7
- package/dist/defaults/hooks/build/post.js +1 -1
- package/dist/defaults/hooks/build/pre.js +1 -1
- package/dist/defaults/hooks/deploy/pre.js +1 -1
- package/dist/defaults/hooks/release/post.js +1 -1
- package/dist/defaults/hooks/release/pre.js +1 -1
- package/dist/defaults/src/assets/js/components/about/index.js +3 -5
- package/dist/defaults/src/assets/js/components/main/index.js +3 -5
- package/dist/defaults/src/assets/js/components/settings/index.js +3 -5
- package/dist/defaults/src/integrations/context-menu/index.js +2 -2
- package/dist/defaults/src/integrations/menu/index.js +3 -3
- package/dist/defaults/src/integrations/tray/index.js +3 -3
- package/dist/defaults/src/main.js +3 -5
- package/dist/defaults/src/preload.js +3 -5
- package/dist/defaults/test/README.md +2 -2
- package/dist/gulp/main.js +9 -10
- package/dist/gulp/tasks/audit.js +7 -7
- package/dist/gulp/tasks/build-config.js +8 -8
- package/dist/gulp/tasks/bundle.js +16 -16
- package/dist/gulp/tasks/defaults.js +3 -3
- package/dist/gulp/tasks/distribute.js +2 -2
- package/dist/gulp/tasks/html.js +9 -9
- package/dist/gulp/tasks/package-quick.js +3 -3
- package/dist/gulp/tasks/package.js +3 -3
- package/dist/gulp/tasks/release.js +3 -3
- package/dist/gulp/tasks/sass.js +6 -6
- package/dist/gulp/tasks/serve.js +4 -4
- package/dist/hooks/notarize-artifacts.js +1 -1
- package/dist/hooks/notarize.js +1 -1
- package/dist/index.js +5 -8
- package/dist/lib/_environment-mixin.js +50 -0
- package/dist/lib/_lifecycle-mixin.js +45 -0
- package/dist/lib/analytics.js +33 -35
- package/dist/lib/app-state.js +14 -14
- package/dist/lib/auth-flow.js +18 -18
- package/dist/lib/auth-persistence.js +12 -12
- package/dist/lib/auth.js +421 -0
- package/dist/lib/auto-updater.js +49 -49
- package/dist/lib/context-menu.js +13 -13
- package/dist/lib/context.js +19 -19
- package/dist/lib/deep-link.js +34 -34
- package/dist/lib/fontawesome.js +5 -5
- package/dist/lib/ipc.js +4 -4
- package/dist/lib/menu.js +25 -25
- package/dist/lib/protocol.js +5 -5
- package/dist/lib/remote-config.js +22 -22
- package/dist/lib/remote-scripts.js +21 -21
- package/dist/lib/restart-manager/index.js +28 -28
- package/dist/lib/sign-helpers/exec-with-limit.js +1 -1
- package/dist/lib/sign-helpers/sign-events.js +1 -1
- package/dist/lib/startup.js +18 -13
- package/dist/lib/storage.js +10 -10
- package/dist/lib/templating.js +16 -16
- package/dist/lib/theme.js +10 -10
- package/dist/lib/tray.js +27 -27
- package/dist/lib/usage.js +11 -11
- package/dist/lib/window-manager.js +26 -26
- package/dist/main.js +398 -483
- package/dist/preload.js +236 -176
- package/dist/renderer.js +417 -391
- package/dist/runner/job-started.js +1 -1
- package/dist/test/fixtures/consumer-app/config/omega.json5 +1 -1
- package/dist/test/fixtures/consumer-app/src/assets/js/components/main/index.js +8 -11
- package/dist/test/fixtures/consumer-app/src/main.js +5 -7
- package/dist/test/fixtures/consumer-app/src/preload.js +2 -2
- package/dist/test/harness/boot-entry.js +22 -20
- package/dist/test/harness/main-entry.js +31 -30
- package/dist/test/harness/renderer-entry.js +5 -5
- package/dist/test/harness/renderer-preload.js +137 -141
- package/dist/test/index.js +10 -10
- package/dist/test/runner.js +2 -2
- package/dist/test/runners/boot.js +7 -6
- package/dist/test/runners/electron.js +3 -2
- package/dist/test/runners/render-event.js +2 -2
- package/dist/test/suites/boot/consumer-app-boots.test.js +57 -26
- package/dist/test/suites/boot/restart-manager.test.js +2 -2
- package/dist/test/suites/boot/storage-bundled.test.js +5 -5
- package/dist/test/suites/boot/theme.test.js +13 -13
- package/dist/test/suites/build/audit.test.js +1 -1
- package/dist/test/suites/build/auth-persistence-resolve.test.js +6 -6
- package/dist/test/suites/build/boot-fixture.test.js +2 -2
- package/dist/test/suites/build/boot-runner-timeout.test.js +6 -5
- package/dist/test/suites/build/brand-scss.test.js +1 -1
- package/dist/test/suites/build/build-json-bake.test.js +1 -1
- package/dist/test/suites/build/cli.test.js +2 -2
- package/dist/test/suites/build/config-schema.test.js +5 -5
- package/dist/test/suites/build/defaults-scaffold.test.js +2 -2
- package/dist/test/suites/build/deploy-hook.test.js +3 -3
- package/dist/test/suites/build/ensure-target.test.js +2 -2
- package/dist/test/suites/build/env-delivery.test.js +2 -2
- package/dist/test/suites/build/esm-only-dependency.test.js +2 -2
- package/dist/test/suites/build/exports.test.js +9 -8
- package/dist/test/suites/build/get-config.test.js +4 -4
- package/dist/test/suites/build/manifest-deps.test.js +1 -1
- package/dist/test/suites/build/merge-line-files.test.js +1 -1
- package/dist/test/suites/build/omega-shell.test.js +34 -2
- package/dist/test/suites/build/omega.test.js +350 -0
- package/dist/test/suites/build/renderer-auth-bridge.test.js +211 -78
- package/dist/test/suites/build/runner.test.js +612 -5
- package/dist/test/suites/build/sentry.test.js +2 -2
- package/dist/test/suites/build/sign-windows-e2e.test.js +2 -2
- package/dist/test/suites/build/templating.test.js +3 -3
- package/dist/test/suites/build/test-stealth.test.js +7 -9
- package/dist/test/suites/build/url-helpers.test.js +55 -56
- package/dist/test/suites/build/validate-config.test.js +2 -2
- package/dist/test/suites/build/wave5-pins.test.js +2 -2
- package/dist/test/suites/main/analytics.test.js +59 -59
- package/dist/test/suites/main/app-state.test.js +66 -66
- package/dist/test/suites/main/auth-flow.test.js +41 -41
- package/dist/test/suites/main/auth-persistence.test.js +54 -43
- package/dist/test/suites/main/{client-bridge.integration.test.js → auth.integration.test.js} +10 -9
- package/dist/test/suites/main/auth.test.js +336 -0
- package/dist/test/suites/main/auto-updater.test.js +134 -134
- package/dist/test/suites/main/boot-sequence.test.js +37 -49
- package/dist/test/suites/main/context-menu.test.js +51 -50
- package/dist/test/suites/main/context.test.js +25 -25
- package/dist/test/suites/main/deep-link.test.js +74 -74
- package/dist/test/suites/main/fontawesome.test.js +27 -27
- package/dist/test/suites/main/ipc.test.js +35 -35
- package/dist/test/suites/main/menu.test.js +101 -100
- package/dist/test/suites/main/protocol.test.js +19 -19
- package/dist/test/suites/main/remote-config.test.js +63 -63
- package/dist/test/suites/main/remote-scripts.test.js +103 -103
- package/dist/test/suites/main/request.test.js +71 -0
- package/dist/test/suites/main/restart-manager.test.js +33 -33
- package/dist/test/suites/main/startup-paths-and-ua.test.js +4 -4
- package/dist/test/suites/main/startup.test.js +26 -26
- package/dist/test/suites/main/stealth-window.test.js +1 -1
- package/dist/test/suites/main/storage.test.js +25 -25
- package/dist/test/suites/main/theme.test.js +34 -34
- package/dist/test/suites/main/tray.test.js +79 -79
- package/dist/test/suites/main/url-helpers.test.js +133 -133
- package/dist/test/suites/main/usage.test.js +25 -25
- package/dist/test/suites/main/window-bounds.test.js +27 -27
- package/dist/test/suites/main/window-manager.test.js +44 -44
- package/dist/test/suites/renderer/analytics-bridge.test.js +5 -5
- package/dist/test/suites/renderer/cross-context-helpers.test.js +37 -31
- package/dist/test/suites/renderer/round-trip.test.js +3 -3
- package/dist/test/suites/renderer/tooltips.test.js +15 -15
- package/dist/test/suites/renderer/{window-em-surface.test.js → window-desktop-surface.test.js} +27 -7
- package/dist/test/utils/extended-mode-warning.js +1 -1
- package/dist/utils/boot-harness.js +56 -0
- package/dist/utils/mode-helpers.js +2 -15
- package/dist/utils/ship-keys.js +3 -3
- package/dist/utils/signing-status.js +51 -0
- package/dist/utils/test-events.js +7 -0
- package/dist/utils/test-stealth.js +6 -6
- package/dist/utils/url-helpers.js +52 -42
- package/dist/utils/user-agent.js +44 -0
- package/dist/vendor/account/engine.js +3 -3
- package/dist/vendor/account/index.js +14 -45
- package/dist/vendor/account/resolve-account.js +44 -0
- package/dist/vendor/account/schema.js +1 -1
- package/dist/vendor/account/user.js +99 -0
- package/dist/vendor/config/client-config.js +1 -1
- package/dist/vendor/config/environment.js +11 -30
- package/dist/vendor/config/index.js +13 -12
- package/dist/vendor/config/load.js +1 -2
- package/dist/vendor/config/platforms.js +1 -1
- package/dist/vendor/config/repo.js +24 -0
- package/dist/vendor/config/retired-keys.js +2 -2
- package/dist/vendor/config/schema.js +5 -8
- package/dist/vendor/config/site-global.js +2 -3
- package/dist/vendor/config/validate.js +1 -2
- package/dist/vendor/config/winback.js +1 -1
- package/dist/vendor/devkit/actions-secrets.js +1 -1
- package/dist/vendor/devkit/attach-log-file.js +1 -1
- package/dist/vendor/devkit/brand-version.js +162 -1
- package/dist/vendor/devkit/build-json.js +1 -1
- package/dist/vendor/devkit/cli-router.js +3 -4
- package/dist/vendor/devkit/defaults-engine.js +9 -11
- package/dist/vendor/devkit/deploy.js +64 -4
- package/dist/vendor/devkit/git-remote.js +78 -1
- package/dist/vendor/devkit/local.js +53 -3
- package/dist/vendor/devkit/lockfile.js +127 -0
- package/dist/vendor/devkit/merge-line-files.js +2 -3
- package/dist/vendor/devkit/pack-local.js +4 -7
- package/dist/vendor/devkit/preludes/origin-heal.js +35 -49
- package/dist/vendor/devkit/target-secrets.js +34 -45
- package/dist/vendor/devkit/test/runner-core.js +6 -6
- package/dist/vendor/monitoring/env.js +2 -2
- package/dist/vendor/monitoring/index.js +1 -1
- package/dist/vendor/monitoring/main.js +1 -1
- package/dist/vendor/monitoring/preload.js +1 -1
- package/dist/vendor/monitoring/renderer.js +1 -1
- package/docs/analytics.md +8 -8
- package/docs/app-state.md +19 -19
- package/docs/audit.md +4 -4
- package/docs/{client-bridge.md → auth.md} +88 -73
- package/docs/auto-updater.md +8 -8
- package/docs/boot-sequence.md +11 -6
- package/docs/build-system.md +1 -1
- package/docs/cdp-debugging.md +1 -1
- package/docs/common-mistakes.md +7 -7
- package/docs/config-schema.md +1 -1
- package/docs/context-menu.md +13 -13
- package/docs/context.md +11 -11
- package/docs/css.md +3 -9
- package/docs/deep-link.md +25 -25
- package/docs/environment-detection.md +16 -16
- package/docs/fontawesome.md +7 -5
- package/docs/hooks.md +8 -8
- package/docs/index.md +39 -28
- package/docs/ipc.md +10 -10
- package/docs/lib-modules.md +9 -9
- package/docs/logging.md +12 -14
- package/docs/menu.md +18 -18
- package/docs/releasing.md +4 -2
- package/docs/remote-config.md +9 -9
- package/docs/remote-scripts.md +12 -12
- package/docs/restart-manager.md +8 -8
- package/docs/runner.md +12 -10
- package/docs/sentry.md +4 -4
- package/docs/shared/analytics.md +1 -1
- package/docs/shared/brands.md +1 -1
- package/docs/shared/breaking-changes.md +79 -13
- package/docs/shared/config.md +24 -21
- package/docs/shared/deploys.md +30 -7
- package/docs/shared/local-dev.md +5 -3
- package/docs/shared/logging.md +1 -1
- package/docs/shared/monitoring.md +5 -5
- package/docs/shared/testing.md +2 -2
- package/docs/shared/theming.md +1 -1
- package/docs/shared/translation.md +19 -10
- package/docs/signing.md +1 -1
- package/docs/startup.md +16 -16
- package/docs/storage.md +11 -11
- package/docs/templating.md +3 -3
- package/docs/test-boot-layer.md +12 -12
- package/docs/test-framework.md +17 -17
- package/docs/themes.md +7 -7
- package/docs/tooltips.md +2 -2
- package/docs/tray.md +31 -31
- package/docs/usage.md +7 -7
- package/docs/verts.md +1 -1
- package/docs/windows.md +22 -22
- package/package.json +3 -4
- package/dist/lib/client-bridge.js +0 -374
- package/dist/lib/logger.js +0 -4
- package/dist/test/suites/build/manager.test.js +0 -213
- package/dist/test/suites/main/client-bridge.test.js +0 -262
package/dist/renderer.js
CHANGED
|
@@ -1,468 +1,494 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Consumer entry (per view): `
|
|
3
|
-
// Reads window.OMEGA_BUILD_JSON.config (the one dist/build.js the view's shell loads, #743),
|
|
1
|
+
// The renderer runtime: ONE ready-made `omega` instance per view, built on @omega.js/client.
|
|
2
|
+
// Consumer entry (per view): `import omega from '@omega.js/desktop/renderer'; omega.initialize()`.
|
|
3
|
+
// Reads window.OMEGA_BUILD_JSON.config (the one dist/build.js the view's shell loads, #743),
|
|
4
|
+
// boots the client it extends, then wires the desktop surfaces.
|
|
4
5
|
//
|
|
5
6
|
// Auth bridge:
|
|
6
7
|
// - On init, asks main "I'm at UID X (or null), are we in sync?" via desktop:auth:sync-request.
|
|
7
|
-
// If main returns a custom token, this renderer
|
|
8
|
+
// If main returns a custom token, this renderer signs in with it (auth.signInWithCustomToken).
|
|
8
9
|
// If main says "sign out," this renderer signs out.
|
|
9
10
|
// - Listens for desktop:auth:sign-in-with-token broadcasts (fired when ANY renderer or main signs in)
|
|
10
11
|
// and signs in with the provided token.
|
|
11
12
|
// - Listens for desktop:auth:sign-out broadcasts and signs out.
|
|
13
|
+
// - Pushes every signed-in state's account to main (desktop:auth:account-resolved), which builds
|
|
14
|
+
// main's `auth.user` from it.
|
|
12
15
|
//
|
|
13
|
-
//
|
|
16
|
+
// Main is the source of truth, renderers reflect.
|
|
14
17
|
|
|
18
|
+
const { Omega: ClientOmega } = require('@omega.js/client');
|
|
19
|
+
const { WAKEUP_ROUTE } = require('@omega.js/client/modules/request.js');
|
|
20
|
+
const { createShell } = require('./assets/js/core/app-shell.js');
|
|
15
21
|
const LoggerLite = require('./lib/logger-lite.js');
|
|
22
|
+
const { getVersion } = require('./utils/mode-helpers.js');
|
|
23
|
+
const { getWebsiteUrl, getAuthUrl } = require('./utils/url-helpers.js');
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The renderer runtime: @omega.js/client's base class (`auth`, `storage`,
|
|
27
|
+
* `analytics`, `bindings`, ...) plus `desktop`, the preload's bridge to the main
|
|
28
|
+
* process. This module exports ONE instance of it; a consumer never writes
|
|
29
|
+
* `new`, and awaits `initialize()` or `ready`.
|
|
30
|
+
*/
|
|
31
|
+
class Omega extends ClientOmega {
|
|
32
|
+
constructor() {
|
|
33
|
+
// The preload exposes the bridge before any page script runs, so its absence
|
|
34
|
+
// is a window built without @omega.js/desktop/preload: a programmer error
|
|
35
|
+
if (typeof window === 'undefined' || !window.desktop) {
|
|
36
|
+
throw new Error('@omega.js/desktop/renderer: window.desktop is missing, so this window did not load the @omega.js/desktop preload (src/preload.js).');
|
|
37
|
+
}
|
|
16
38
|
|
|
17
|
-
|
|
18
|
-
const self = this;
|
|
19
|
-
|
|
20
|
-
self.config = null;
|
|
21
|
-
self.logger = new LoggerLite('renderer');
|
|
22
|
-
self.omega = null;
|
|
39
|
+
super();
|
|
23
40
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
41
|
+
// Everything that crosses to the main process, under main's names: ipc,
|
|
42
|
+
// storage (the APP store; `omega.storage` stays the client's page store),
|
|
43
|
+
// theme, fontawesome, autoUpdater, analytics, context, usage, remoteConfig
|
|
44
|
+
this.desktop = window.desktop;
|
|
45
|
+
this.logger = new LoggerLite('renderer');
|
|
46
|
+
}
|
|
27
47
|
|
|
28
|
-
|
|
29
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Boot the client from the view's build snapshot plus `overrides`, then wire
|
|
50
|
+
* the desktop surfaces: the auth bridge, theme controls, the app shell,
|
|
51
|
+
* icons, tooltips and verts.
|
|
52
|
+
* @param {object} [overrides] - runtime config merged over the build snapshot.
|
|
53
|
+
* @returns {Promise<Omega>} the instance.
|
|
54
|
+
*/
|
|
55
|
+
async initialize(overrides) {
|
|
56
|
+
// Merge runtime overrides on top of build-time config.
|
|
57
|
+
// OMEGA_BUILD_JSON comes off the window: the view's shell loads the ONE
|
|
58
|
+
// `dist/build.js` ahead of this bundle (#743), which is also what makes it
|
|
59
|
+
// readable from DevTools.
|
|
60
|
+
const config = Object.assign({}, window.OMEGA_BUILD_JSON?.config || {}, overrides || {});
|
|
61
|
+
|
|
62
|
+
// The RUNNING environment beats the baked one, exactly as it does in main
|
|
63
|
+
// ([#925](https://github.com/Omega-JS-Stack/omega/issues/925)). A page has no
|
|
64
|
+
// `process`, so `isTesting()` here reads `config.environment`, the word the
|
|
65
|
+
// BUILD was for; the preload does have it and hands it over on the bridge, so a
|
|
66
|
+
// test lane booting a production artifact gets the same answer in this renderer
|
|
67
|
+
// that main gives. No second signal: it is the one input, one context removed.
|
|
68
|
+
if (this.desktop.environment) {
|
|
69
|
+
config.environment = this.desktop.environment;
|
|
70
|
+
}
|
|
30
71
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
72
|
+
this.logger.log('Initializing @omega.js/desktop (renderer)...');
|
|
73
|
+
|
|
74
|
+
// `analyticsBridge` is the preload's analytics surface, handed over as config:
|
|
75
|
+
// a renderer NEVER sends analytics itself. The client forwards every event
|
|
76
|
+
// through this bridge to main, whose sender owns the one device id
|
|
77
|
+
// (electron-store, MAC-seeded), the one session id minted per launch, and the
|
|
78
|
+
// real engagement time ([#411](https://github.com/Omega-JS-Stack/omega/issues/411)).
|
|
79
|
+
// The client reads this key alone, so nothing but THIS line can bridge it.
|
|
80
|
+
//
|
|
81
|
+
// 🚫 The GA Measurement Protocol secret must NEVER be injected into this
|
|
82
|
+
// config. A second sender in a renderer would split one install into two GA
|
|
83
|
+
// clients (the #396 class), which is why desktop's config carries the
|
|
84
|
+
// measurement id alone, and why the client drops any secret handed to a
|
|
85
|
+
// bridged renderer.
|
|
86
|
+
await super.initialize(Object.assign({}, config, {
|
|
87
|
+
analyticsBridge: this.desktop.analytics,
|
|
88
|
+
}));
|
|
89
|
+
|
|
90
|
+
// Wire the auth bridge: sync with main, listen for broadcasts.
|
|
91
|
+
await this._wireAuthBridge();
|
|
92
|
+
|
|
93
|
+
// Wire declarative theme controls ([data-omega-theme-set]).
|
|
94
|
+
this._wireThemeControls();
|
|
95
|
+
|
|
96
|
+
// The vendored app shell (@omega.js/web's core/js/core/app-shell.js), built
|
|
97
|
+
// from this instance exactly as web builds it
|
|
98
|
+
this.shell = createShell(this);
|
|
99
|
+
|
|
100
|
+
// Auto-render FontAwesome icons (<i class="fa-solid fa-*">) and Bootstrap
|
|
101
|
+
// tooltips ([data-bs-toggle="tooltip"]): presence-driven, live via
|
|
102
|
+
// MutationObserver, zero consumer setup.
|
|
103
|
+
this.enableFontAwesome();
|
|
104
|
+
this._wireTooltips();
|
|
105
|
+
|
|
106
|
+
// Auto-bind [data-omega-vert] elements to the client verts module
|
|
107
|
+
// (house/company lane only, no AdSense in desktop surfaces).
|
|
108
|
+
this._wireAds();
|
|
109
|
+
|
|
110
|
+
this.logger.log('@omega.js/desktop (renderer) initialized.');
|
|
111
|
+
|
|
112
|
+
return this;
|
|
50
113
|
}
|
|
51
114
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
//
|
|
115
|
+
// Declarative theme switching: any element with `data-omega-theme-set="system|light|dark"`
|
|
116
|
+
// becomes a theme control: clicking it calls main's theme setter (persisted, applied to
|
|
117
|
+
// every renderer live). The `<html data-bs-theme>` attribute itself is maintained by the
|
|
118
|
+
// preload's theme applier (src/preload.js); consumers just drop plain buttons:
|
|
55
119
|
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
// (electron-store, MAC-seeded), the one session id minted per launch, and the
|
|
60
|
-
// real engagement time ([#411](https://github.com/Omega-JS-Stack/omega/issues/411)).
|
|
61
|
-
// The client reads this key alone, so nothing but THIS line can bridge it.
|
|
120
|
+
// <button data-omega-theme-set="light">Day</button>
|
|
121
|
+
// <button data-omega-theme-set="dark">Dusk</button>
|
|
122
|
+
// <button data-omega-theme-set="system">Auto</button>
|
|
62
123
|
//
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// bridged renderer.
|
|
68
|
-
try {
|
|
69
|
-
const wmMod = require('@omega.js/client');
|
|
70
|
-
self.omega = wmMod.default || wmMod;
|
|
71
|
-
if (self.omega?.initialize) {
|
|
72
|
-
await self.omega.initialize(Object.assign({}, self.config, {
|
|
73
|
-
analyticsBridge: (typeof window !== 'undefined' && window.desktop?.analytics) || null,
|
|
74
|
-
}));
|
|
124
|
+
// Delegated on document so controls rendered after initialize still work.
|
|
125
|
+
_wireThemeControls() {
|
|
126
|
+
if (typeof document === 'undefined') {
|
|
127
|
+
return;
|
|
75
128
|
}
|
|
76
|
-
|
|
77
|
-
|
|
129
|
+
|
|
130
|
+
document.addEventListener('click', (event) => {
|
|
131
|
+
const control = event.target.closest('[data-omega-theme-set]');
|
|
132
|
+
if (!control) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const source = control.getAttribute('data-omega-theme-set');
|
|
136
|
+
this.desktop.theme.set(source).catch((e) => {
|
|
137
|
+
this.logger.warn(`theme set '${source}' failed:`, e?.message);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
78
140
|
}
|
|
79
141
|
|
|
80
|
-
//
|
|
81
|
-
|
|
142
|
+
// FontAwesome auto-render: any `<i>` element carrying `fa-*` classes gets the
|
|
143
|
+
// Font Awesome SVG injected inline, whether present at init, inserted later,
|
|
144
|
+
// or re-classed via JS at any time (`el.className = 'fa-solid fa-stop'`
|
|
145
|
+
// re-renders in place). The whole DOM mechanism (class parsing on FA's
|
|
146
|
+
// family × weight model, MutationObserver, caching, re-render/clear) is
|
|
147
|
+
// @omega.js/client's icon renderer, started through the instance's `icons`
|
|
148
|
+
// module, the SAME one web pages run; desktop only supplies the transport
|
|
149
|
+
// (IPC to main's icon server). Unknown names leave the element empty (marked
|
|
150
|
+
// data-omega-fa); consumers that want a fallback check `omega.desktop.fontawesome.get()`.
|
|
151
|
+
//
|
|
152
|
+
// <i class="fa-solid fa-rocket me-2"></i> → <i …><svg …>…</svg></i>
|
|
153
|
+
//
|
|
154
|
+
// Public because a minimal surface that skips initialize() can still enable
|
|
155
|
+
// it: `omega.enableFontAwesome()` on the imported instance.
|
|
156
|
+
enableFontAwesome() {
|
|
157
|
+
if (typeof document === 'undefined') {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
82
160
|
|
|
83
|
-
|
|
84
|
-
|
|
161
|
+
this.icons.start({
|
|
162
|
+
resolve: (name, style) => this.desktop.fontawesome.get(name, style),
|
|
163
|
+
}, document);
|
|
164
|
+
}
|
|
85
165
|
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
166
|
+
// Bootstrap tooltips: auto-initialize every `[data-bs-toggle="tooltip"]`
|
|
167
|
+
// element (Bootstrap's JS + Popper ship inside @omega.js/desktop as a prebuilt bundle,
|
|
168
|
+
// assets/js/bootstrap.bundle.js; consumers add ZERO setup).
|
|
169
|
+
// Live-managed via MutationObserver:
|
|
170
|
+
// - elements inserted later get their tooltip on arrival
|
|
171
|
+
// - `data-bs-title` / `title` changes update the live instance (emptying it
|
|
172
|
+
// disposes: no tooltip is a valid state)
|
|
173
|
+
// - removed elements have their instance disposed (no orphaned tips)
|
|
174
|
+
// The full Bootstrap namespace is exposed at `window.bootstrap` (and
|
|
175
|
+
// `omega.bootstrap`) for manual control: Tooltip, Collapse, Dropdown, etc.
|
|
176
|
+
// Only tooltips are auto-initialized (Bootstrap 5 makes them opt-in); the other
|
|
177
|
+
// components' standard data-api works out of the box on Bootstrap markup.
|
|
178
|
+
//
|
|
179
|
+
// Gotcha (Bootstrap's own): disabled elements don't fire hover events, so wrap a
|
|
180
|
+
// disabled control in a <span data-bs-toggle="tooltip"> to tooltip it.
|
|
181
|
+
_wireTooltips() {
|
|
182
|
+
if (typeof document === 'undefined') {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
91
185
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
186
|
+
// Everything, including the require, is deferred until the document
|
|
187
|
+
// exists: the Bootstrap bundle reads document.documentElement at import
|
|
188
|
+
// time, which is null when this runs from a preload (the @omega.js/desktop test harness
|
|
189
|
+
// does) before the DOM is built.
|
|
190
|
+
const start = () => {
|
|
191
|
+
let Tooltip;
|
|
192
|
+
try {
|
|
193
|
+
// Prebuilt UMD (Popper inlined): loads through the bundle task AND plain require().
|
|
194
|
+
const bootstrap = require('./assets/js/bootstrap.bundle.js');
|
|
195
|
+
Tooltip = bootstrap.Tooltip;
|
|
196
|
+
window.bootstrap = Object.assign(window.bootstrap || {}, bootstrap);
|
|
197
|
+
} catch (e) {
|
|
198
|
+
this.logger.warn('bootstrap Tooltip unavailable: tooltips disabled.', e?.message);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
95
201
|
|
|
96
|
-
|
|
202
|
+
this.bootstrap = window.bootstrap;
|
|
97
203
|
|
|
98
|
-
|
|
99
|
-
};
|
|
204
|
+
const SELECTOR = '[data-bs-toggle="tooltip"]';
|
|
100
205
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// preload's theme applier (src/preload.js) — consumers just drop plain buttons:
|
|
105
|
-
//
|
|
106
|
-
// <button data-omega-theme-set="light">Day</button>
|
|
107
|
-
// <button data-omega-theme-set="dark">Dusk</button>
|
|
108
|
-
// <button data-omega-theme-set="system">Auto</button>
|
|
109
|
-
//
|
|
110
|
-
// Delegated on document so controls rendered after initialize still work.
|
|
111
|
-
Manager.prototype._wireThemeControls = function () {
|
|
112
|
-
const self = this;
|
|
206
|
+
const init = (el) => {
|
|
207
|
+
Tooltip.getOrCreateInstance(el);
|
|
208
|
+
};
|
|
113
209
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
210
|
+
const scan = (root) => {
|
|
211
|
+
if (root.matches?.(SELECTOR)) {
|
|
212
|
+
init(root);
|
|
213
|
+
}
|
|
214
|
+
root.querySelectorAll?.(SELECTOR).forEach(init);
|
|
215
|
+
};
|
|
117
216
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
//
|
|
140
|
-
// <i class="fa-solid fa-rocket me-2"></i> → <i …><svg …>…</svg></i>
|
|
141
|
-
Manager.prototype._wireFontAwesome = function () {
|
|
142
|
-
const self = this;
|
|
217
|
+
const dispose = (root) => {
|
|
218
|
+
if (root.nodeType !== 1) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const candidates = [root, ...(root.querySelectorAll?.(SELECTOR) || [])];
|
|
222
|
+
for (const el of candidates) {
|
|
223
|
+
Tooltip.getInstance(el)?.dispose();
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
new MutationObserver((mutations) => {
|
|
228
|
+
for (const mutation of mutations) {
|
|
229
|
+
if (mutation.type === 'childList') {
|
|
230
|
+
mutation.addedNodes.forEach((node) => {
|
|
231
|
+
if (node.nodeType === 1) {
|
|
232
|
+
scan(node);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
mutation.removedNodes.forEach(dispose);
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
143
238
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
239
|
+
// Attribute change on a (potential) tooltip host.
|
|
240
|
+
const el = mutation.target;
|
|
241
|
+
const instance = Tooltip.getInstance(el);
|
|
242
|
+
if (!el.matches(SELECTOR)) {
|
|
243
|
+
instance?.dispose();
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (!instance) {
|
|
247
|
+
init(el);
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
if (mutation.attributeName !== 'data-bs-toggle') {
|
|
251
|
+
// data-bs-original-title is Bootstrap's OWN bookkeeping: the
|
|
252
|
+
// Tooltip constructor MOVES a plain `title` attribute there.
|
|
253
|
+
// Without reading it, a title-only host infinite-loops the
|
|
254
|
+
// renderer: init removes `title` → this handler sees no title →
|
|
255
|
+
// dispose → dispose RESTORES `title` → re-init → …, a pure
|
|
256
|
+
// MutationObserver microtask storm that starves the main thread
|
|
257
|
+
// (found by Somiibo's session-limits boot suite: the settings
|
|
258
|
+
// page froze solid on one badge).
|
|
259
|
+
const title = el.getAttribute('data-bs-title') || el.getAttribute('title') || el.getAttribute('data-bs-original-title');
|
|
260
|
+
if (title) {
|
|
261
|
+
instance.setContent({ '.tooltip-inner': title });
|
|
262
|
+
} else {
|
|
263
|
+
instance.dispose();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}).observe(document.documentElement, {
|
|
268
|
+
childList: true,
|
|
269
|
+
subtree: true,
|
|
270
|
+
attributes: true,
|
|
271
|
+
attributeFilter: ['data-bs-toggle', 'data-bs-title', 'title'],
|
|
272
|
+
});
|
|
147
273
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
resolve: (name, style) => self.ipc.invoke('desktop:fontawesome:get', { name, style })
|
|
151
|
-
.then((r) => r?.svg ?? null),
|
|
152
|
-
}).start(document);
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
// Public alias — minimal surfaces that skip the full initialize() (no
|
|
156
|
-
// @omega.js/client / auth bridge) can still enable the FontAwesome auto-render:
|
|
157
|
-
// new (require('@omega.js/desktop/renderer'))().enableFontAwesome();
|
|
158
|
-
Manager.prototype.enableFontAwesome = Manager.prototype._wireFontAwesome;
|
|
159
|
-
|
|
160
|
-
// Bootstrap tooltips — auto-initialize every `[data-bs-toggle="tooltip"]`
|
|
161
|
-
// element (Bootstrap's JS + Popper ship inside @omega.js/desktop as a prebuilt bundle —
|
|
162
|
-
// assets/js/bootstrap.bundle.js; consumers add ZERO setup).
|
|
163
|
-
// Live-managed via MutationObserver:
|
|
164
|
-
// - elements inserted later get their tooltip on arrival
|
|
165
|
-
// - `data-bs-title` / `title` changes update the live instance (emptying it
|
|
166
|
-
// disposes — no tooltip is a valid state)
|
|
167
|
-
// - removed elements have their instance disposed (no orphaned tips)
|
|
168
|
-
// The full Bootstrap namespace is exposed at `window.bootstrap` (and
|
|
169
|
-
// `manager.bootstrap`) for manual control — Tooltip, Collapse, Dropdown, etc.
|
|
170
|
-
// Only tooltips are auto-initialized (Bootstrap 5 makes them opt-in); the other
|
|
171
|
-
// components' standard data-api works out of the box on Bootstrap markup.
|
|
172
|
-
//
|
|
173
|
-
// Gotcha (Bootstrap's own): disabled elements don't fire hover events — wrap a
|
|
174
|
-
// disabled control in a <span data-bs-toggle="tooltip"> to tooltip it.
|
|
175
|
-
Manager.prototype._wireTooltips = function () {
|
|
176
|
-
const self = this;
|
|
274
|
+
scan(document.documentElement);
|
|
275
|
+
};
|
|
177
276
|
|
|
178
|
-
|
|
179
|
-
|
|
277
|
+
if (document.documentElement && document.readyState !== 'loading') {
|
|
278
|
+
start();
|
|
279
|
+
} else {
|
|
280
|
+
document.addEventListener('DOMContentLoaded', start, { once: true });
|
|
281
|
+
}
|
|
180
282
|
}
|
|
181
283
|
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
284
|
+
// Vert auto-bind (ads-system phase 4): every `[data-omega-vert]` element,
|
|
285
|
+
// present at init or inserted later (MutationObserver, same liveness as the
|
|
286
|
+
// FontAwesome/tooltip wiring), is handed to the client's verts module,
|
|
287
|
+
// which owns the WHOLE lifecycle: lazy arming near the viewport, sandboxed
|
|
288
|
+
// iframe to the resolved in-house source's /omega/verts/serve, origin-validated
|
|
289
|
+
// postMessage, host-owned rotation + staleness recovery, no-fill collapse.
|
|
290
|
+
// The type is PINNED to 'house': desktop surfaces never run the AdSense
|
|
291
|
+
// provider lane (policy: no web context), so even a shared omega.json5 that
|
|
292
|
+
// carries `advertising.providers.adsense` can only ever take the
|
|
293
|
+
// house/company inventory here. mount() merges passed options OVER the
|
|
294
|
+
// element attributes, so the pin is absolute; bound hosts are marked
|
|
295
|
+
// `data-omega-vert-bound="house"` for observability.
|
|
296
|
+
//
|
|
297
|
+
// <div data-omega-vert data-omega-vert-size="banner"></div>
|
|
298
|
+
_wireAds() {
|
|
299
|
+
if (this._adsWired || typeof document === 'undefined' || typeof MutationObserver === 'undefined') {
|
|
195
300
|
return;
|
|
196
301
|
}
|
|
302
|
+
this._adsWired = true;
|
|
197
303
|
|
|
198
|
-
|
|
304
|
+
const SELECTOR = '[data-omega-vert]';
|
|
199
305
|
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
Tooltip.getOrCreateInstance(el);
|
|
306
|
+
const mount = (el) => {
|
|
307
|
+
this.verts.mount(el, { type: 'house' });
|
|
308
|
+
el.setAttribute('data-omega-vert-bound', 'house');
|
|
204
309
|
};
|
|
205
310
|
|
|
206
311
|
const scan = (root) => {
|
|
207
312
|
if (root.matches?.(SELECTOR)) {
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
root.querySelectorAll?.(SELECTOR).forEach(init);
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
const dispose = (root) => {
|
|
214
|
-
if (root.nodeType !== 1) {
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
const candidates = [root, ...(root.querySelectorAll?.(SELECTOR) || [])];
|
|
218
|
-
for (const el of candidates) {
|
|
219
|
-
Tooltip.getInstance(el)?.dispose();
|
|
313
|
+
mount(root);
|
|
220
314
|
}
|
|
315
|
+
root.querySelectorAll?.(SELECTOR).forEach(mount);
|
|
221
316
|
};
|
|
222
317
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
318
|
+
const start = () => {
|
|
319
|
+
new MutationObserver((mutations) => {
|
|
320
|
+
for (const mutation of mutations) {
|
|
226
321
|
mutation.addedNodes.forEach((node) => {
|
|
227
322
|
if (node.nodeType === 1) {
|
|
228
323
|
scan(node);
|
|
229
324
|
}
|
|
230
325
|
});
|
|
231
|
-
mutation.removedNodes.forEach(dispose);
|
|
232
|
-
continue;
|
|
233
326
|
}
|
|
327
|
+
}).observe(document.documentElement, { childList: true, subtree: true });
|
|
234
328
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const instance = Tooltip.getInstance(el);
|
|
238
|
-
if (!el.matches(SELECTOR)) {
|
|
239
|
-
instance?.dispose();
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
if (!instance) {
|
|
243
|
-
init(el);
|
|
244
|
-
continue;
|
|
245
|
-
}
|
|
246
|
-
if (mutation.attributeName !== 'data-bs-toggle') {
|
|
247
|
-
// data-bs-original-title is Bootstrap's OWN bookkeeping: the
|
|
248
|
-
// Tooltip constructor MOVES a plain `title` attribute there.
|
|
249
|
-
// Without reading it, a title-only host infinite-loops the
|
|
250
|
-
// renderer: init removes `title` → this handler sees no title →
|
|
251
|
-
// dispose → dispose RESTORES `title` → re-init → … — a pure
|
|
252
|
-
// MutationObserver microtask storm that starves the main thread
|
|
253
|
-
// (found by Somiibo's session-limits boot suite: the settings
|
|
254
|
-
// page froze solid on one badge).
|
|
255
|
-
const title = el.getAttribute('data-bs-title') || el.getAttribute('title') || el.getAttribute('data-bs-original-title');
|
|
256
|
-
if (title) {
|
|
257
|
-
instance.setContent({ '.tooltip-inner': title });
|
|
258
|
-
} else {
|
|
259
|
-
instance.dispose();
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}).observe(document.documentElement, {
|
|
264
|
-
childList: true,
|
|
265
|
-
subtree: true,
|
|
266
|
-
attributes: true,
|
|
267
|
-
attributeFilter: ['data-bs-toggle', 'data-bs-title', 'title'],
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
scan(document.documentElement);
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
if (document.documentElement && document.readyState !== 'loading') {
|
|
274
|
-
start();
|
|
275
|
-
} else {
|
|
276
|
-
document.addEventListener('DOMContentLoaded', start, { once: true });
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
// Vert auto-bind (ads-system phase 4) — every `[data-omega-vert]` element,
|
|
281
|
-
// present at init or inserted later (MutationObserver, same liveness as the
|
|
282
|
-
// FontAwesome/tooltip wiring), is handed to @omega.js/client's verts module,
|
|
283
|
-
// which owns the WHOLE lifecycle: lazy arming near the viewport, sandboxed
|
|
284
|
-
// iframe to the resolved in-house source's /omega/verts/serve, origin-validated
|
|
285
|
-
// postMessage, host-owned rotation + staleness recovery, no-fill collapse.
|
|
286
|
-
// The type is PINNED to 'house': desktop surfaces never run the AdSense
|
|
287
|
-
// provider lane (policy: no web context), so even a shared omega.json5 that
|
|
288
|
-
// carries `advertising.providers.adsense` can only ever take the
|
|
289
|
-
// house/company inventory here. mount() merges passed options OVER the
|
|
290
|
-
// element attributes, so the pin is absolute; bound hosts are marked
|
|
291
|
-
// `data-omega-vert-bound="house"` for observability.
|
|
292
|
-
//
|
|
293
|
-
// <div data-omega-vert data-omega-vert-size="banner"></div>
|
|
294
|
-
Manager.prototype._wireAds = function () {
|
|
295
|
-
const self = this;
|
|
296
|
-
|
|
297
|
-
if (self._adsWired || typeof document === 'undefined' || typeof MutationObserver === 'undefined' || !self.omega?.verts) {
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
self._adsWired = true;
|
|
301
|
-
|
|
302
|
-
const SELECTOR = '[data-omega-vert]';
|
|
303
|
-
|
|
304
|
-
const mount = (el) => {
|
|
305
|
-
self.omega.verts().mount(el, { type: 'house' });
|
|
306
|
-
el.setAttribute('data-omega-vert-bound', 'house');
|
|
307
|
-
};
|
|
329
|
+
scan(document.documentElement);
|
|
330
|
+
};
|
|
308
331
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
332
|
+
if (document.documentElement && document.readyState !== 'loading') {
|
|
333
|
+
start();
|
|
334
|
+
} else {
|
|
335
|
+
document.addEventListener('DOMContentLoaded', start, { once: true });
|
|
312
336
|
}
|
|
313
|
-
root.querySelectorAll?.(SELECTOR).forEach(mount);
|
|
314
|
-
};
|
|
315
|
-
|
|
316
|
-
const start = () => {
|
|
317
|
-
new MutationObserver((mutations) => {
|
|
318
|
-
for (const mutation of mutations) {
|
|
319
|
-
mutation.addedNodes.forEach((node) => {
|
|
320
|
-
if (node.nodeType === 1) {
|
|
321
|
-
scan(node);
|
|
322
|
-
}
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
}).observe(document.documentElement, { childList: true, subtree: true });
|
|
326
|
-
|
|
327
|
-
scan(document.documentElement);
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
if (document.documentElement && document.readyState !== 'loading') {
|
|
331
|
-
start();
|
|
332
|
-
} else {
|
|
333
|
-
document.addEventListener('DOMContentLoaded', start, { once: true });
|
|
334
|
-
}
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
// Bridge between renderer's @omega.js/client and main's client-bridge.
|
|
338
|
-
// Mirrors BXM's foreground sync logic.
|
|
339
|
-
Manager.prototype._wireAuthBridge = async function () {
|
|
340
|
-
const self = this;
|
|
341
|
-
if (!self.ipc) return;
|
|
342
|
-
|
|
343
|
-
// Warm the backend before any of the waits below. This bridge ends in a
|
|
344
|
-
// sync-request, and main answers it by POSTing `/omega/user/token` — the
|
|
345
|
-
// app's first backend call, on a function that is cold every time the app
|
|
346
|
-
// launches. Fire-and-forget and unauthenticated: the backend answers a wakeup
|
|
347
|
-
// before it loads a route or authenticates
|
|
348
|
-
// ([#644](https://github.com/Omega-JS-Stack/omega/issues/644)).
|
|
349
|
-
// The route is required where it is used, the way client-bridge requires the
|
|
350
|
-
// request layer it shares with this bridge.
|
|
351
|
-
try {
|
|
352
|
-
const { WAKEUP_ROUTE } = require('@omega.js/client/modules/request.js');
|
|
353
|
-
self.omega?.request?.(WAKEUP_ROUTE, { wakeup: true });
|
|
354
|
-
} catch (e) {
|
|
355
|
-
self.logger.warn('wakeup ping failed (non-fatal):', e?.message);
|
|
356
337
|
}
|
|
357
338
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
339
|
+
// Bridge between this renderer's auth and main's lib/auth.js.
|
|
340
|
+
async _wireAuthBridge() {
|
|
341
|
+
const { ipc } = this.desktop;
|
|
342
|
+
|
|
343
|
+
// The auth click triggers the extension's pages carry, routed to main:
|
|
344
|
+
// `omega-signin` opens the sign-in flow, `omega-account` the brand's
|
|
345
|
+
// /account page, both in the user's browser. `omega-signout` is the
|
|
346
|
+
// client's, and it runs this instance's signOut(): main signs out, and this
|
|
347
|
+
// window's own sign-out arrives on the broadcast below.
|
|
348
|
+
this.triggers.register('signin', () => ipc.invoke('desktop:auth:open-flow'));
|
|
349
|
+
this.triggers.register('account', () => ipc.invoke('desktop:auth:open-account'));
|
|
350
|
+
|
|
351
|
+
// Warm the backend before any of the waits below. This bridge ends in a
|
|
352
|
+
// sync-request, and main answers it by POSTing `/omega/user/token`, the
|
|
353
|
+
// app's first backend call, on a function that is cold every time the app
|
|
354
|
+
// launches. Fire-and-forget and unauthenticated: the backend answers a wakeup
|
|
355
|
+
// before it loads a route or authenticates
|
|
356
|
+
// ([#644](https://github.com/Omega-JS-Stack/omega/issues/644)). A context
|
|
357
|
+
// with no resolvable API base (a dev build with no stack) skips it.
|
|
367
358
|
try {
|
|
368
|
-
|
|
369
|
-
self.logger.log('signed in via broadcast token.');
|
|
359
|
+
this.request(WAKEUP_ROUTE, { wakeup: true });
|
|
370
360
|
} catch (e) {
|
|
371
|
-
|
|
361
|
+
this.logger.warn('wakeup ping failed (non-fatal):', e?.message);
|
|
372
362
|
}
|
|
373
|
-
});
|
|
374
363
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
364
|
+
// Listen for sign-in-with-token broadcasts (main signed in via deep link, or another renderer signed in).
|
|
365
|
+
ipc.on('desktop:auth:sign-in-with-token', async ({ token }) => {
|
|
366
|
+
if (!token) return;
|
|
367
|
+
try {
|
|
368
|
+
await this.auth.signInWithCustomToken(token);
|
|
369
|
+
this.logger.log('signed in via broadcast token.');
|
|
370
|
+
} catch (e) {
|
|
371
|
+
this.logger.error('signInWithCustomToken (broadcast) failed:', e?.message);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
385
374
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
375
|
+
// Listen for sign-out broadcasts.
|
|
376
|
+
ipc.on('desktop:auth:sign-out', async () => {
|
|
377
|
+
try {
|
|
378
|
+
await this.auth.signOut();
|
|
379
|
+
this.logger.log('signed out via broadcast.');
|
|
380
|
+
} catch (e) {
|
|
381
|
+
this.logger.error('signOut (broadcast) failed:', e?.message);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// The client's FULL auth cycle already runs: each state
|
|
386
|
+
// change fetches the Firestore account and lands one `User`, which the
|
|
387
|
+
// data-omega-bind bindings read. Every signed-in state's account is pushed
|
|
388
|
+
// to main (desktop:auth:account-resolved): main can't run Firestore, so this
|
|
389
|
+
// is how its `auth.user` learns the REAL account (browser contexts
|
|
390
|
+
// resolve, the authority caches). Main uid-guards the push.
|
|
391
|
+
let lastUser = null;
|
|
392
|
+
|
|
393
|
+
const push = async (user) => {
|
|
394
|
+
if (!user.authenticated) return;
|
|
401
395
|
try {
|
|
402
|
-
await
|
|
403
|
-
uid,
|
|
404
|
-
|
|
405
|
-
|
|
396
|
+
await ipc.invoke('desktop:auth:account-resolved', {
|
|
397
|
+
uid: user.uid,
|
|
398
|
+
document: user.toJSON(),
|
|
399
|
+
identity: identityOf(user),
|
|
406
400
|
});
|
|
407
401
|
} catch (e) {
|
|
408
|
-
|
|
402
|
+
this.logger.warn('account-resolved push failed (non-fatal):', e?.message);
|
|
409
403
|
}
|
|
410
404
|
};
|
|
411
405
|
|
|
406
|
+
this.auth.listen((state) => {
|
|
407
|
+
lastUser = state.user;
|
|
408
|
+
push(state.user);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
// Re-push when MAIN's auth state changes: a renderer that resolved BEFORE main
|
|
412
|
+
// signed in had its push uid-rejected (correctly: main was signed out). When
|
|
413
|
+
// main comes up on the same user, offer the account again.
|
|
414
|
+
ipc.on('desktop:auth:state-changed', (identity) => {
|
|
415
|
+
if (identity && lastUser && lastUser.uid === identity.uid) {
|
|
416
|
+
push(lastUser);
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// Sync with main on load. If main has a different state, it'll send back instructions.
|
|
412
421
|
try {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
422
|
+
const current = this.firebaseAuth ? this.firebaseAuth.currentUser : null;
|
|
423
|
+
const result = await ipc.invoke('desktop:auth:sync-request', {
|
|
424
|
+
contextUid: current ? current.uid : null,
|
|
416
425
|
});
|
|
426
|
+
|
|
427
|
+
if (!result?.needsSync) return;
|
|
428
|
+
|
|
429
|
+
if (result.signOut) {
|
|
430
|
+
await this.auth.signOut();
|
|
431
|
+
this.logger.log('synced: signed out (main was signed out).');
|
|
432
|
+
} else if (result.customToken) {
|
|
433
|
+
await this.auth.signInWithCustomToken(result.customToken);
|
|
434
|
+
this.logger.log(`synced: signed in as ${result.user?.email || result.user?.uid}.`);
|
|
435
|
+
}
|
|
417
436
|
} catch (e) {
|
|
418
|
-
|
|
437
|
+
this.logger.warn('auth sync failed (non-fatal):', e?.message);
|
|
419
438
|
}
|
|
439
|
+
}
|
|
420
440
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
441
|
+
/**
|
|
442
|
+
* Sign out through main, so main and every renderer sign out together (the
|
|
443
|
+
* desktop:auth:sign-out broadcast). The `.omega-signout` trigger runs this;
|
|
444
|
+
* `omega.auth.signOut()` signs out this renderer alone.
|
|
445
|
+
* @returns {Promise<{ success: boolean, error?: string }>}
|
|
446
|
+
*/
|
|
447
|
+
signOut() {
|
|
448
|
+
return this.desktop.ipc.invoke('desktop:auth:sign-out');
|
|
429
449
|
}
|
|
430
450
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
451
|
+
/**
|
|
452
|
+
* Main's account: `{ uid, document, identity }`, main's authoritative answer
|
|
453
|
+
* rather than this renderer's own Firebase session.
|
|
454
|
+
* @returns {Promise<{ uid: string|null, document: object, identity: object|null }>}
|
|
455
|
+
*/
|
|
456
|
+
getMainUser() {
|
|
457
|
+
return this.desktop.ipc.invoke('desktop:auth:get-user');
|
|
458
|
+
}
|
|
436
459
|
|
|
437
|
-
|
|
460
|
+
// Desktop's own helpers beside the client's environment four and
|
|
461
|
+
// getFunctionsUrl/getApiUrl: the same plain functions main and preload call
|
|
462
|
+
getVersion() {
|
|
463
|
+
return getVersion();
|
|
464
|
+
}
|
|
438
465
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
self.logger.log('synced: signed out (main was signed out).');
|
|
442
|
-
} else if (result.customToken && auth?.signInWithCustomToken) {
|
|
443
|
-
await auth.signInWithCustomToken(result.customToken);
|
|
444
|
-
self.logger.log(`synced: signed in as ${result.user?.email || result.user?.uid}.`);
|
|
445
|
-
}
|
|
446
|
-
} catch (e) {
|
|
447
|
-
self.logger.warn('auth sync failed (non-fatal):', e?.message);
|
|
466
|
+
getWebsiteUrl(environment) {
|
|
467
|
+
return getWebsiteUrl(this, environment);
|
|
448
468
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
return
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
+
|
|
470
|
+
getAuthUrl(environment, returnUrl) {
|
|
471
|
+
return getAuthUrl(this, environment, returnUrl);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* The identity a `User` was built from, the shape main builds its own from.
|
|
477
|
+
* @param {object} user - a signed-in `User`.
|
|
478
|
+
* @returns {{ uid: string, email: string|null, displayName: string|null, photoURL: string|null, emailVerified: boolean }}
|
|
479
|
+
*/
|
|
480
|
+
function identityOf(user) {
|
|
481
|
+
return {
|
|
482
|
+
uid: user.uid,
|
|
483
|
+
email: user.email,
|
|
484
|
+
displayName: user.profile.displayName,
|
|
485
|
+
photoURL: user.profile.photoURL,
|
|
486
|
+
emailVerified: user.profile.emailVerified,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// The ONE instance, initialized by the view's entry
|
|
491
|
+
const omega = new Omega();
|
|
492
|
+
|
|
493
|
+
module.exports = omega;
|
|
494
|
+
module.exports.Omega = Omega;
|