@omega.js/desktop 0.52.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 +17 -12
- 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 +11 -11
- 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/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/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 +2 -2
- 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 +8 -11
- package/dist/vendor/config/load.js +1 -2
- package/dist/vendor/config/platforms.js +1 -1
- 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/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/local.js +2 -0
- package/dist/vendor/devkit/merge-line-files.js +2 -3
- 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 +38 -27
- 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/remote-config.md +9 -9
- package/docs/remote-scripts.md +12 -12
- package/docs/restart-manager.md +8 -8
- package/docs/sentry.md +4 -4
- package/docs/shared/analytics.md +1 -1
- package/docs/shared/breaking-changes.md +79 -13
- package/docs/shared/config.md +17 -21
- 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/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/docs/startup.md
CHANGED
|
@@ -24,7 +24,7 @@ The default behavior — `mode: 'normal'` + `openAtLogin: { enabled: true, mode:
|
|
|
24
24
|
|
|
25
25
|
### `normal` (default)
|
|
26
26
|
|
|
27
|
-
Standard app behavior. Your `main.js` calls `windows.create('main', { show: !startup.isLaunchHidden() })` from inside `
|
|
27
|
+
Standard app behavior. Your `main.js` calls `windows.create('main', { show: !startup.isLaunchHidden() })` from inside `omega.initialize().then(...)`. In `normal` mode, `show` is `true` so the window appears immediately. Dock visible (macOS), taskbar entry (win/linux).
|
|
28
28
|
|
|
29
29
|
### `hidden`
|
|
30
30
|
|
|
@@ -38,31 +38,31 @@ Use this for: menubar apps, agent apps (clipboard managers, time trackers, syste
|
|
|
38
38
|
|
|
39
39
|
> **Note:** the deprecated `'tray-only'` mode is no longer valid — its behavior was always identical to `'hidden'`, so they've been folded into one. Old `tray-only` configs fall back to `'normal'` per `getMode()` validation.
|
|
40
40
|
|
|
41
|
-
## Public API on `
|
|
41
|
+
## Public API on `omega.startup`
|
|
42
42
|
|
|
43
43
|
```js
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
omega.startup.getMode() // user-launch mode: 'normal' | 'hidden'
|
|
45
|
+
omega.startup.isLaunchHidden() // true if THIS launch is hidden: combines
|
|
46
46
|
// user-launch mode + login-launch detection.
|
|
47
47
|
// Use this in main.js to gate windows.create().
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
omega.startup.wasLaunchedAtLogin() // true if the OS auto-launched us at login
|
|
49
|
+
omega.startup.applyEarly() // calls app.dock.hide() if needed (called by main.js boot)
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
omega.startup.setOpenAtLogin(true) // back-compat boolean form
|
|
52
|
+
omega.startup.setOpenAtLogin({ enabled: true, mode: 'hidden' }) // object form
|
|
53
|
+
omega.startup.isOpenAtLogin() // read live OS state
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
## Typical main.js pattern
|
|
57
57
|
|
|
58
58
|
```js
|
|
59
|
-
|
|
59
|
+
omega.initialize().then(() => {
|
|
60
60
|
// Always create the main window. In hidden launches, `show: false` keeps it
|
|
61
61
|
// invisible until something explicitly calls windows.show('main') — but it's
|
|
62
62
|
// in the registry, so @omega.js/desktop's activate/second-instance handlers can find and
|
|
63
63
|
// surface it when the user double-clicks the running app.
|
|
64
|
-
|
|
65
|
-
show: !
|
|
64
|
+
omega.windows.create('main', {
|
|
65
|
+
show: !omega.startup.isLaunchHidden(),
|
|
66
66
|
});
|
|
67
67
|
});
|
|
68
68
|
```
|
|
@@ -71,9 +71,9 @@ Don't conditionally skip `create()` for hidden launches — without `main` in th
|
|
|
71
71
|
|
|
72
72
|
## Boot order
|
|
73
73
|
|
|
74
|
-
`startup.applyEarly()` is the **first** call in `
|
|
74
|
+
`startup.applyEarly()` is the **first** call in `omega.initialize()`: before `whenReady`, before any other lib. The goal: spend as little time as possible in the dock-bounce window.
|
|
75
75
|
|
|
76
|
-
Sequence: applyEarly → before-quit hook → ipc → storage → sentry → protocol → deep-link → app-state → whenReady → updater → tray/menu/contextMenu → startup.initialize →
|
|
76
|
+
Sequence: applyEarly → before-quit hook → ipc → storage → theme → fontawesome → sentry → protocol → deep-link → auth-flow → app-state → context → usage → whenReady → updater → tray/menu/contextMenu → startup.initialize → auth → remote-config → remote-scripts → analytics → restart-manager → windows.initialize (full list: [boot-sequence.md](boot-sequence.md)). **@omega.js/desktop does not auto-create the main window**: your `main.js` does that inside the `.then()` callback after `initialize()` resolves.
|
|
77
77
|
|
|
78
78
|
## How zero-bounce works on macOS
|
|
79
79
|
|
|
@@ -86,7 +86,7 @@ Sequence: applyEarly → before-quit hook → ipc → storage → sentry → pro
|
|
|
86
86
|
|
|
87
87
|
With the key baked, a MANUAL launch also starts dockless — the dock icon appears the moment the main window surfaces (every surface path runs `_ensureDockVisible()` → `app.dock.show()`), so the visible difference is only that the bounce animation is replaced by the icon appearing when the window is ready.
|
|
88
88
|
|
|
89
|
-
At runtime, when the consumer first calls `
|
|
89
|
+
At runtime, when the consumer first calls `omega.windows.show()` (or the `windows.create()` call resolves with `show: true`), @omega.js/desktop calls `app.dock.show()` so the dock icon appears alongside the window. Reverses cleanly via `app.dock.hide()` if you want to go back to invisible.
|
|
90
90
|
|
|
91
91
|
The injection is YAML-text-level (preserves comments, idempotent, merges with existing `extendInfo`). See `src/gulp/tasks/build-config.js`.
|
|
92
92
|
|
|
@@ -136,7 +136,7 @@ Hidden / agent apps usually want:
|
|
|
136
136
|
And in `src/integrations/tray/index.js`:
|
|
137
137
|
|
|
138
138
|
```js
|
|
139
|
-
tray.update('open', { click: () =>
|
|
139
|
+
tray.update('open', { click: () => omega.windows.show('main') });
|
|
140
140
|
```
|
|
141
141
|
|
|
142
142
|
The window is created at boot but invisible. When the user clicks the tray's "Open" item (or double-clicks the app icon), `windows.show('main')` runs, @omega.js/desktop calls `app.dock.show()`, and the user sees both the dock icon and the window appear together.
|
package/docs/storage.md
CHANGED
|
@@ -13,13 +13,13 @@ Linux: ~/.config/<productName>/omega-storage.json
|
|
|
13
13
|
## Main-process API (sync, direct disk-backed)
|
|
14
14
|
|
|
15
15
|
```js
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
omega.storage.get(key, defaultValue) // any
|
|
17
|
+
omega.storage.set(key, value)
|
|
18
|
+
omega.storage.delete(key)
|
|
19
|
+
omega.storage.has(key) // boolean
|
|
20
|
+
omega.storage.clear()
|
|
21
|
+
omega.storage.onChange(key, fn) // returns unsubscribe fn
|
|
22
|
+
omega.storage.getPath() // absolute path to omega-storage.json
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
## Renderer-process API (async, proxied through preload + IPC)
|
|
@@ -41,19 +41,19 @@ off();
|
|
|
41
41
|
Keys support dot-notation for nested objects natively:
|
|
42
42
|
|
|
43
43
|
```js
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
omega.storage.set('window.main.bounds', { x: 10, y: 20, w: 800, h: 600 });
|
|
45
|
+
omega.storage.get('window.main.bounds.w'); // → 800
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
## Change broadcasts
|
|
49
49
|
|
|
50
50
|
Every `set` / `delete` / `clear` in main broadcasts an `desktop:storage:change` IPC event to all renderer windows. The renderer's `window.desktop.storage.onChange` filters by key locally.
|
|
51
51
|
|
|
52
|
-
In main, `
|
|
52
|
+
In main, `omega.storage.onChange(key, fn)` registers a callback fired with `(value, previous)`.
|
|
53
53
|
|
|
54
54
|
## Implementation notes
|
|
55
55
|
|
|
56
|
-
- Storage initialization is async
|
|
56
|
+
- Storage initialization is async: `omega.initialize()` `await`s it before any other lib boots, since features like `app-state` and `windows` rely on it.
|
|
57
57
|
- IPC handlers (`desktop:storage:get` etc.) are registered on the @omega.js/desktop `ipc` bus, not directly on `ipcMain`. See [ipc.md](ipc.md).
|
|
58
58
|
- The store uses `name: 'omega-storage'` (filename `omega-storage.json`). Don't reuse this name in a separate `electron-store` instance.
|
|
59
59
|
- `electron-store@11` is ESM-only. The bundler inlines it INTO `main.bundle.js` (the static-specifier `import()` in `lib/storage.js`) — consumers install NOTHING; packaged apps carry it inside the bundle with no runtime resolution. (It used to be a runtime import the bundler was told to ignore, which silently no-op'd storage in packaged consumers — @omega.js/desktop is a devDependency and never ships in the asar.)
|
package/docs/templating.md
CHANGED
|
@@ -77,13 +77,13 @@ Drop your own `config/page-template.html` in your project root. @omega.js/deskto
|
|
|
77
77
|
|
|
78
78
|
## Runtime API
|
|
79
79
|
|
|
80
|
-
`
|
|
80
|
+
`omega.templating` is also available at runtime if you need to template a string yourself (e.g. dynamic deep-link routes):
|
|
81
81
|
|
|
82
82
|
```js
|
|
83
|
-
|
|
83
|
+
omega.templating.render('Hello {{ user.name }}', { user: { name: 'Ian' } });
|
|
84
84
|
// → 'Hello Ian'
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
omega.templating.render('Custom [name]', { name: 'X' }, { brackets: ['[', ']'] });
|
|
87
87
|
// → 'Custom X'
|
|
88
88
|
```
|
|
89
89
|
|
package/docs/test-boot-layer.md
CHANGED
|
@@ -11,12 +11,12 @@ The build and the boot happen in a **staged app root of their own**, `<project>/
|
|
|
11
11
|
| `build` | Plain Node — config parsing, util fns, schema validation. | Fast (ms) |
|
|
12
12
|
| `main` | @omega.js/desktop lib code in isolation (storage, ipc, tray, etc.) inside Electron. | Fast (~50ms each) |
|
|
13
13
|
| `renderer` | Inside a hidden BrowserWindow. | Fast |
|
|
14
|
-
| `boot` | The **whole boot integration
|
|
14
|
+
| `boot` | The **whole boot integration**: consumer's main.js → omega.initialize → live state | ~1s startup, then fast |
|
|
15
15
|
|
|
16
16
|
Use `boot` for tests that need to verify **integration** rather than unit behavior:
|
|
17
17
|
- "Does the consumer's `src/main.js` actually wire up correctly?"
|
|
18
18
|
- "Did all 13 boot steps complete without throwing?"
|
|
19
|
-
- "Did config flow from JSON5 →
|
|
19
|
+
- "Did config flow from JSON5 → omega.config → tray titles?"
|
|
20
20
|
- "Did `src/integrations/{tray,menu,context-menu}/index.js` load?"
|
|
21
21
|
- "Is the menu rendered with the expected default ids?"
|
|
22
22
|
|
|
@@ -31,17 +31,17 @@ module.exports = {
|
|
|
31
31
|
timeout: 20000,
|
|
32
32
|
tests: [
|
|
33
33
|
{
|
|
34
|
-
description: '
|
|
35
|
-
inspect: async ({
|
|
36
|
-
expect(
|
|
37
|
-
expect(
|
|
34
|
+
description: 'omega initialized end-to-end',
|
|
35
|
+
inspect: async ({ omega, expect, projectRoot }) => {
|
|
36
|
+
expect(omega._initialized).toBe(true);
|
|
37
|
+
expect(omega.config).toBeTruthy();
|
|
38
38
|
},
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
41
|
description: 'tray + menu rendered',
|
|
42
|
-
inspect: async ({
|
|
43
|
-
expect(
|
|
44
|
-
expect(
|
|
42
|
+
inspect: async ({ omega, expect }) => {
|
|
43
|
+
expect(omega.tray.has('open')).toBe(true);
|
|
44
|
+
expect(omega.menu.isRendered()).toBe(true);
|
|
45
45
|
},
|
|
46
46
|
},
|
|
47
47
|
],
|
|
@@ -51,7 +51,7 @@ module.exports = {
|
|
|
51
51
|
The `inspect` function receives:
|
|
52
52
|
| Arg | Description |
|
|
53
53
|
|---|---|
|
|
54
|
-
| `
|
|
54
|
+
| `omega` | The fully-initialized live main-process instance, the same one your consumer code uses. |
|
|
55
55
|
| `expect` | @omega.js/desktop's [Jest-compatible assertion library](../src/test/assert.js). |
|
|
56
56
|
| `projectRoot` | Absolute path to the consumer project root (its `src/`, `config/` — and the `dist/` a boot run must never write). |
|
|
57
57
|
| `appRoot` | Absolute path to the staged app root Electron booted — `<projectRoot>/.omega/test-app`. Assert on built artifacts here (`<appRoot>/dist/main.bundle.js`), not under `projectRoot`. |
|
|
@@ -67,7 +67,7 @@ The `inspect` function receives:
|
|
|
67
67
|
- `OMEGA_TEST_BOOT=1` — gate
|
|
68
68
|
- `OMEGA_TEST_BOOT_HARNESS=<absolute path to dist/test/harness/boot-entry.js>`
|
|
69
69
|
- `OMEGA_TEST_BOOT_SPEC=<temp file with test definitions>`
|
|
70
|
-
5. @omega.js/desktop's `main.js` boots normally; after `
|
|
70
|
+
5. @omega.js/desktop's `main.js` boots normally; after `omega.initialize()` resolves, detects `OMEGA_TEST_BOOT=1`, reconstitutes each `inspect` from its serialized body string, runs them sequentially, and emits `__OMEGA_TEST__` JSON lines on stdout (the ONE prefix, `TEST_EVENT_PREFIX` in [src/utils/test-events.js](../src/utils/test-events.js)).
|
|
71
71
|
6. Test runner parses results, calls `app.exit()`. **No sleep, no kill.**
|
|
72
72
|
|
|
73
73
|
## View suites in this lane
|
|
@@ -152,6 +152,6 @@ The `build`/`main`/`renderer` layers cover @omega.js/desktop's lib code fast and
|
|
|
152
152
|
|
|
153
153
|
## Limitations
|
|
154
154
|
|
|
155
|
-
- Tests run sequentially in a single Electron process to amortize startup cost (~1s). State doesn't carry across tests
|
|
155
|
+
- Tests run sequentially in a single Electron process to amortize startup cost (~1s). State doesn't carry across tests: they all share one `omega` instance.
|
|
156
156
|
- `inspect` function bodies are serialized via `Function.prototype.toString` and reconstituted with `new Function(...)`. Closures over the test file's outer scope **don't survive** — only the `inspect` argument bag is available inside.
|
|
157
157
|
- We can't simulate user input (clicking the tray, right-clicking, typing). For that, you'd need `nut-js` or similar — out of scope.
|
package/docs/test-framework.md
CHANGED
|
@@ -4,16 +4,16 @@ Built-in test framework for both @omega.js/desktop itself and consumer projects.
|
|
|
4
4
|
|
|
5
5
|
## 🚫 NEVER mock — test against the real harness (HARD RULE)
|
|
6
6
|
|
|
7
|
-
**Do NOT hand-roll fake/stub/mock objects
|
|
7
|
+
**Do NOT hand-roll fake/stub/mock objects**: no mock `omega`, fake `ipc`/`storage`/`window`/`tray`, stubbed `app`/`BrowserWindow`, or fake IPC channels. Every test gets the **real** framework context:
|
|
8
8
|
|
|
9
9
|
- `build` runs real @omega.js/desktop helper code in plain Node.
|
|
10
|
-
- `main` / `renderer` / `boot` run inside a **real spawned Electron process**, where `ctx.
|
|
10
|
+
- `main` / `renderer` / `boot` run inside a **real spawned Electron process**, where `ctx.omega` (and boot's `inspect({ omega })`) is the **real booted main-process instance**: real `omega.storage`, `omega.ipc`, `omega.tray`, `omega.windows`, etc. Use them; exercise the code the way production does.
|
|
11
11
|
|
|
12
12
|
**Pure functions are the ONLY exception.** A function with zero I/O (config-defaults merge, icon-path resolver, schema validator, CLI alias resolver, a string/number transform) can be `require()`d and called directly with plain inputs — that's not mocking, there's nothing to mock. The moment a function touches `app.*` / `BrowserWindow` / `ipcMain` / `Tray` / the real bundle / an external service, it MUST run against the real harness in the appropriate layer (`main` / `renderer` / `boot`), not a stub.
|
|
13
13
|
|
|
14
14
|
**Real external APIs are gated behind extended mode (`TEST_EXTENDED_MODE`), NOT mocked** (see [Extended vs normal mode](#extended-vs-normal-mode) below). Normal mode skips them *in the source*; extended mode runs them for real. The test never fakes them. **Anything an extended test creates in a real external system MUST be cleaned up** by the test (via the suite's `cleanup(ctx)` hook) — external systems are not reset between runs.
|
|
15
15
|
|
|
16
|
-
If you find yourself writing `const mockX = {...}` to satisfy code under test, STOP
|
|
16
|
+
If you find yourself writing `const mockX = {...}` to satisfy code under test, STOP: pass the real `ctx.omega` (or its real sub-object), or, if the function is genuinely pure, call it directly with plain data.
|
|
17
17
|
|
|
18
18
|
### The ONLY two exceptions where a narrow stub is allowed
|
|
19
19
|
|
|
@@ -30,7 +30,7 @@ A feature is not done when it works — it's done when every surface it exposes
|
|
|
30
30
|
|
|
31
31
|
| Coverage | Layer | Proves |
|
|
32
32
|
|---|---|---|
|
|
33
|
-
| **Logic** | `build` / `main` | The feature's functions do the right thing when called directly (real
|
|
33
|
+
| **Logic** | `build` / `main` | The feature's functions do the right thing when called directly (the real booted `omega`, real storage, real IPC) |
|
|
34
34
|
| **UI** | `renderer` | The feature's interface is WIRED — a real event on the real DOM triggers the behavior and the visible result appears |
|
|
35
35
|
| **End-to-end** | `boot` | The feature survives in the consumer's actual built bundle (extend the boot suite's `inspect` assertions) |
|
|
36
36
|
|
|
@@ -85,7 +85,7 @@ TEST_EXTENDED_MODE=true npx omega test build/config
|
|
|
85
85
|
|
|
86
86
|
The target matches against the test file path. The source prefix scopes selection to framework-only or project-only tests — a prefixed target excludes the other source entirely:
|
|
87
87
|
|
|
88
|
-
- `mgr
|
|
88
|
+
- `mgr:`: the **universal cross-framework alias** for "the framework's own tests" (framework-only). Works identically in @omega.js/desktop, @omega.js/extension, @omega.js/web, and @omega.js/backend.
|
|
89
89
|
- `desktop:` / `framework:` — desktop-specific aliases for framework-only tests, equivalent to `mgr:`.
|
|
90
90
|
- `project:` — consumer project tests only.
|
|
91
91
|
|
|
@@ -111,20 +111,20 @@ Anything an extended suite creates externally must be torn down in its `cleanup(
|
|
|
111
111
|
|
|
112
112
|
### `OMEGA_ENVIRONMENT=testing`, the one input a test run names
|
|
113
113
|
|
|
114
|
-
Both @omega.js/desktop test runners (`runners/electron.js`, `runners/boot.js`) spawn their child with `OMEGA_ENVIRONMENT=testing`, the ONE environment input ([#817](https://github.com/Omega-JS-Stack/omega/issues/817)), and nothing writes over an explicit one: the word a build baked into the artifact is the fallback for a packaged app, never an override ([#925](https://github.com/Omega-JS-Stack/omega/issues/925)). That powers `
|
|
114
|
+
Both @omega.js/desktop test runners (`runners/electron.js`, `runners/boot.js`) spawn their child with `OMEGA_ENVIRONMENT=testing`, the ONE environment input ([#817](https://github.com/Omega-JS-Stack/omega/issues/817)), and nothing writes over an explicit one: the word a build baked into the artifact is the fallback for a packaged app, never an override ([#925](https://github.com/Omega-JS-Stack/omega/issues/925)). That powers `omega.isTesting()` (and the build module's `isTesting()`), the cross-context helper everything in @omega.js/desktop checks when it needs to behave differently in tests:
|
|
115
115
|
|
|
116
116
|
- `auto-updater` flips its idle threshold from 15min → 3s and its periodic tick from 60s → 500ms, AND short-circuits the native install-prompt dialog (so tests don't pop modal windows).
|
|
117
117
|
- **Every BrowserWindow surfaces stealth** — named windows via `window-manager._surface()` AND raw `new BrowserWindow()` ones (e.g. a consumer's automation popup) via a global `browser-window-created` hook registered in `main.js` step 1a-ii. The shared recipe lives in `src/utils/stealth-window.js`: shown INACTIVE (keyboard focus never leaves your editor), opacity 0, click-through (`setIgnoreMouseEvents`), and raw windows get `show()` rerouted to `showInactive()` + `focus()` no-op'd — a test run never interrupts you, and a stray real click physically can't land in the app, while synthetic test input (`executeJavaScript`, `sendInputEvent`, CDP) is unaffected. **`webContents.focus()` is suppressed separately** (a global `web-contents-created` hook in the same main.js block): it bypasses the window-level patches — it's a different object whose `focus()` reaches the native window directly and makes the invisible window KEY, grabbing the keyboard mid-typing even under the accessory policy (which only prevents *launch* activation, not key-window steals). Consumers call it legitimately (e.g. address-bar focus on tab select), so it's no-op'd per-contents under the same predicate. Set **`OMEGA_TEST_SHOW=1`** to surface windows normally and watch a run live (the predicate is evaluated per window, so flipping it mid-run works). Deliberately NOT `hide()`/`minimize()`: occluded windows get throttled by Chromium (`requestAnimationFrame` pauses, `document.visibilityState` flips to `hidden`) — tests would exercise a DIFFERENT runtime, whereas an opacity-0 shown-inactive window renders and behaves identically to a visible one. See [windows.md](windows.md).
|
|
118
118
|
- **App-level activation is suppressed too (macOS).** Launching a regular-policy app activates it — the menu bar and keyboard focus switch to the test process at launch even though every window surfaces inactive. Under the same stealth predicate (`src/utils/test-stealth.js` — Testing mode + `OMEGA_TEST_SHOW` unset), `main.js` (step 1a, before app ready) and the spawned test harness flip the app to the **accessory activation policy** (`app.dock.hide()` — the same switch `LSUIElement` bakes for packaged hidden-mode apps): the process never activates, never shows a dock icon, and never steals focus, while windows still render identically. `OMEGA_TEST_SHOW=1` restores normal activation along with visible windows. Verified by frontmost-app sampling across a full run: zero focus changes (previously four steals per run).
|
|
119
119
|
- `main.js#initialize` isolates userData per environment: testing runs get `<userData> (Testing)` — **wiped at boot**, so every test run starts from a clean slate (post-run state stays on disk for inspection until the next run; set `OMEGA_TEST_KEEP_USERDATA=1` to skip the wipe). Dev runs get ` (Development)`; production is untouched. See [boot-sequence.md](boot-sequence.md).
|
|
120
120
|
- **The main-layer harness exposes a CDP endpoint.** `test/harness/main-entry.js` appends `--remote-debugging-port=0` at require time (loopback-only, OS-assigned port) and publishes the resolved port as **`process.env.OMEGA_CDP_PORT`** before suites run (read from Chromium's `DevToolsActivePort` file; any value inherited from your shell is overwritten — that one points at your dev app, not the harness). Consumer suites can drive real browser automation against the harness Electron itself, e.g. `playwright-core`'s `connectOverCDP('http://127.0.0.1:' + process.env.OMEGA_CDP_PORT)`. Covered by `suites/main/harness-cdp.test.js`.
|
|
121
|
-
- Other lib code can branch on `
|
|
121
|
+
- Other lib code can branch on `omega.isTesting()` to suppress dock bounce, login-item changes, OS protocol-handler registration, etc.
|
|
122
122
|
|
|
123
123
|
Consumers writing their own tests name the same input in their own runner, for example in `package.json`:
|
|
124
124
|
```json
|
|
125
125
|
"test": "OMEGA_ENVIRONMENT=testing vitest"
|
|
126
126
|
```
|
|
127
|
-
Then in your code, gate test-only behavior on `
|
|
127
|
+
Then in your code, gate test-only behavior on `omega.isTesting()` instead of inventing yet another env var.
|
|
128
128
|
|
|
129
129
|
## Test discovery
|
|
130
130
|
|
|
@@ -170,14 +170,14 @@ module.exports = {
|
|
|
170
170
|
layer: 'main', // 'build' | 'main' | 'renderer'
|
|
171
171
|
description: 'storage (main)',
|
|
172
172
|
cleanup: async (ctx) => { // runs after the last test
|
|
173
|
-
ctx.
|
|
173
|
+
ctx.omega.storage.clear();
|
|
174
174
|
},
|
|
175
175
|
tests: [
|
|
176
176
|
{
|
|
177
177
|
name: 'set + get round-trip',
|
|
178
178
|
run: (ctx) => {
|
|
179
|
-
ctx.
|
|
180
|
-
ctx.expect(ctx.
|
|
179
|
+
ctx.omega.storage.set('hello', 'world');
|
|
180
|
+
ctx.expect(ctx.omega.storage.get('hello')).toBe('world');
|
|
181
181
|
},
|
|
182
182
|
},
|
|
183
183
|
{
|
|
@@ -232,7 +232,7 @@ module.exports = [
|
|
|
232
232
|
| Layer | Where it runs | Use for |
|
|
233
233
|
|---|---|---|
|
|
234
234
|
| `build` | Plain Node | CLI, package.json, config schema, gulp tasks |
|
|
235
|
-
| `main` | Spawned Electron main process |
|
|
235
|
+
| `main` | Spawned Electron main process | `omega.initialize()`, lib modules, IPC, windows |
|
|
236
236
|
| `renderer` | Hidden BrowserWindow: the framework's harness page by default, one of the project's own `src/views/` when the suite declares `view: '<name>'` | `window.desktop.*`, preload bridge, UI logic, your own views |
|
|
237
237
|
|
|
238
238
|
The runner partitions test files by layer at discovery time. The build layer runs inline; the main layer spawns Electron once with all main suites and parses JSON-line stdout.
|
|
@@ -246,7 +246,7 @@ ctx.expect(actual) // Jest-compatible expect()
|
|
|
246
246
|
ctx.state // shared object across tests in a suite/group
|
|
247
247
|
ctx.layer // 'build' | 'main' | 'renderer'
|
|
248
248
|
ctx.skip(reason) // skip from inside the test
|
|
249
|
-
ctx.
|
|
249
|
+
ctx.omega // (main layer only) the booted @omega.js/desktop main-process instance
|
|
250
250
|
```
|
|
251
251
|
|
|
252
252
|
## expect() matchers
|
|
@@ -290,15 +290,15 @@ All test output is also teed (ANSI-stripped) to `<projectRoot>/logs/test.log`, t
|
|
|
290
290
|
## Test harness internals (main layer)
|
|
291
291
|
|
|
292
292
|
- `runners/electron.js` spawns Electron with `harness/main-entry.js` as the app.
|
|
293
|
-
- `harness/main-entry.js` boots
|
|
293
|
+
- `harness/main-entry.js` boots `omega` with `skipWindowCreation: true`, runs the suites, emits results via `__OMEGA_TEST__{json}\n` lines on stdout (`TEST_EVENT_PREFIX`, [src/utils/test-events.js](../src/utils/test-events.js), the one prefix every harness writes and every runner reads).
|
|
294
294
|
- The runner parses those lines and renders @omega.js/backend-style output.
|
|
295
295
|
- stderr is always drained (otherwise the pipe fills and the harness blocks); printed only when `OMEGA_TEST_DEBUG=1`.
|
|
296
296
|
- `ELECTRON_RUN_AS_NODE` is stripped from the spawn env (would otherwise make Electron behave as Node and break the harness).
|
|
297
|
-
- **Consumer-scheme containment.** The consumer's `main.js`
|
|
297
|
+
- **Consumer-scheme containment.** The consumer's `main.js` (where the real `protocol.handle('<brand.id>', …)` lives) never runs in this harness, so `brand://` URLs loaded by main-layer suites would be UNHANDLED. Chromium treats a load on an unhandled scheme as an *external protocol* and hands it to the OS: and if an installed copy of the app owns the scheme (e.g. `/Applications/Brand.app` on macOS, registered via its `Info.plist`), Launch Services launches the installed production app mid-test-run. The harness contains this two ways after `omega` boots: (1) it registers a stub handler for the consumer's brand scheme (read from `<cwd>/config/omega.json5`) on the default session, so `brand://` loads commit in-process as a blank page; (2) it denies the `openExternal` permission on the default session and every session created after it (partitions included), so no navigation can hand ANY scheme to the OS during a test run. Suites that need real page content for `brand://` URLs belong in the boot layer, where the real app (and its real protocol handler) runs. A main-layer suite that genuinely needs its OWN handler for the brand scheme must call `protocol.unhandle(brand.id)` first: the harness stub holds the scheme on the default session.
|
|
298
298
|
|
|
299
299
|
## A test run never touches the OS keychain, and a blocked boot reports
|
|
300
300
|
|
|
301
|
-
The harness resolves auth persistence to the **`none`** strategy on every layer, main and boot alike, before any config is read: a brand that declares `omega.authPersistence: 'safeStorage'` still runs its tests with Firebase auth in memory, because the lanes never sign a real user in. That is a hard rule, not a nicety. The boot lane spawns the raw, unsigned `Electron` binary, which carries no keychain ACL, so one `safeStorage` read parks the entire run behind a macOS SecurityAgent prompt ([#907](https://github.com/Omega-JS-Stack/omega/issues/907)). ONE signal answers "is this a test run" on both layers, `
|
|
301
|
+
The harness resolves auth persistence to the **`none`** strategy on every layer, main and boot alike, before any config is read: a brand that declares `omega.authPersistence: 'safeStorage'` still runs its tests with Firebase auth in memory, because the lanes never sign a real user in. That is a hard rule, not a nicety. The boot lane spawns the raw, unsigned `Electron` binary, which carries no keychain ACL, so one `safeStorage` read parks the entire run behind a macOS SecurityAgent prompt ([#907](https://github.com/Omega-JS-Stack/omega/issues/907)). ONE signal answers "is this a test run" on both layers, `omega.isTesting()`: the boot lane boots the consumer's PRODUCTION artifact, and the word baked into it no longer writes over the `OMEGA_ENVIRONMENT=testing` the runner spawned it with ([#925](https://github.com/Omega-JS-Stack/omega/issues/925)), so the booted app reports the lane it is running in. And since a boot can block long before `main.js` requires the harness (where the per-test timeout lives), the boot runner carries its own budget, measured as SILENCE: every harness line rearms it, so a suite that legitimately runs longer keeps going, and a boot that produces no harness output for 60s (`OMEGA_TEST_BOOT_TIMEOUT_MS` overrides) counts its tests failed, prints `✗ boot: no harness output after 60s; last runtime.log line: "<line>"` from the booted app's `logs/runtime.log`, and ends the child instead of hanging `npx omega test`.
|
|
302
302
|
|
|
303
303
|
## Writing consumer tests
|
|
304
304
|
|
|
@@ -314,7 +314,7 @@ module.exports = {
|
|
|
314
314
|
{
|
|
315
315
|
name: 'storage starts empty',
|
|
316
316
|
run: (ctx) => {
|
|
317
|
-
ctx.expect(ctx.
|
|
317
|
+
ctx.expect(ctx.omega.storage.get('user')).toBeUndefined();
|
|
318
318
|
},
|
|
319
319
|
},
|
|
320
320
|
],
|
package/docs/themes.md
CHANGED
|
@@ -87,13 +87,13 @@ theme: {
|
|
|
87
87
|
}
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
## Appearance (light / dark / system)
|
|
90
|
+
## Appearance (light / dark / system): `omega.theme`
|
|
91
91
|
|
|
92
92
|
@omega.js/desktop owns appearance at runtime. `config.theme.appearance` is only the **app default**; the resolved appearance is applied and kept live by the theme lib:
|
|
93
93
|
|
|
94
94
|
- **`'system'` (default)** follows the OS preference **live** — when the OS flips, every page updates without a reload or restart.
|
|
95
95
|
- **`'light'` / `'dark'`** are explicit overrides.
|
|
96
|
-
- A user's runtime choice (`
|
|
96
|
+
- A user's runtime choice (`omega.theme.set(...)`) is **persisted in `omega.storage`** (`theme.appearance`) and wins over the config default on every boot.
|
|
97
97
|
|
|
98
98
|
### How it propagates
|
|
99
99
|
|
|
@@ -105,10 +105,10 @@ The applier is **opt-in by presence**: it only manages pages whose `<html>` alre
|
|
|
105
105
|
|
|
106
106
|
```js
|
|
107
107
|
// Main
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const unsub =
|
|
108
|
+
omega.theme.get(); // 'system' | 'light' | 'dark' (the chosen source)
|
|
109
|
+
omega.theme.resolved(); // 'light' | 'dark' (what's showing)
|
|
110
|
+
omega.theme.set('dark'); // apply + persist (throws on invalid values)
|
|
111
|
+
const unsub = omega.theme.onChange(({ source, resolved }) => { ... });
|
|
112
112
|
|
|
113
113
|
// Renderer (any page with the @omega.js/desktop preload)
|
|
114
114
|
await window.desktop.theme.get(); // { source, resolved }
|
|
@@ -120,7 +120,7 @@ Main also broadcasts `desktop:theme:changed { source, resolved }` to BrowserWind
|
|
|
120
120
|
|
|
121
121
|
### Declarative controls
|
|
122
122
|
|
|
123
|
-
Any element with `data-omega-theme-set` becomes a theme switch (wired by the renderer
|
|
123
|
+
Any element with `data-omega-theme-set` becomes a theme switch (wired by the renderer's `omega.initialize()`, event-delegated, so late-rendered controls work):
|
|
124
124
|
|
|
125
125
|
```html
|
|
126
126
|
<button data-omega-theme-set="light">Day</button>
|
package/docs/tooltips.md
CHANGED
|
@@ -52,7 +52,7 @@ control and put the tooltip on the wrapper:
|
|
|
52
52
|
## The rest of Bootstrap's JS
|
|
53
53
|
|
|
54
54
|
The full namespace is exposed at **`window.bootstrap`** (and
|
|
55
|
-
`
|
|
55
|
+
`omega.bootstrap`): `Tooltip`, `Popover`, `Collapse`, `Dropdown`, `Modal`,
|
|
56
56
|
`Offcanvas`, `Tab`, `Toast`, `Alert`, `Button`, `Carousel`, `ScrollSpy`. Only
|
|
57
57
|
tooltips are auto-initialized; the other components' standard **data-api**
|
|
58
58
|
works out of the box on plain Bootstrap markup (e.g.
|
|
@@ -94,6 +94,6 @@ runs from a preload, e.g. the test harness).
|
|
|
94
94
|
|
|
95
95
|
- `src/test/suites/renderer/tooltips.test.js` — bundle loads, auto-init on
|
|
96
96
|
insertion, live retitle, dispose-on-removal, tip cleanup. (The harness wires
|
|
97
|
-
the renderer
|
|
97
|
+
the renderer instance in the preload world: see the suite header for the
|
|
98
98
|
world-split notes; single-world hover behavior is covered by consumer boot
|
|
99
99
|
suites.)
|
package/docs/tray.md
CHANGED
|
@@ -4,19 +4,19 @@ File-based tray/menubar. @omega.js/desktop looks for `src/integrations/tray/inde
|
|
|
4
4
|
|
|
5
5
|
## Config
|
|
6
6
|
|
|
7
|
-
No config block. Path is conventional: `src/integrations/tray/index.js`. To opt out, call `
|
|
7
|
+
No config block. Path is conventional: `src/integrations/tray/index.js`. To opt out, call `omega.tray.disable()` from your main entry: idempotent, tears down any existing Tray.
|
|
8
8
|
|
|
9
9
|
## Definition file
|
|
10
10
|
|
|
11
11
|
```js
|
|
12
12
|
// src/integrations/tray/index.js
|
|
13
|
-
module.exports = ({
|
|
13
|
+
module.exports = ({ omega, tray }) => {
|
|
14
14
|
// @omega.js/desktop auto-resolves the tray icon from config/icons/<platform>/tray.png at build
|
|
15
15
|
// time, so explicit tray.icon() is OPTIONAL. Call it only to override.
|
|
16
16
|
// Note: on macOS, if you pass your own path, the filename MUST end in
|
|
17
17
|
// `Template.png` for the OS to auto-invert it in dark mode.
|
|
18
18
|
// tray.icon('src/assets/icons/my-trayTemplate.png');
|
|
19
|
-
tray.tooltip(
|
|
19
|
+
tray.tooltip(omega.config?.app?.productName);
|
|
20
20
|
|
|
21
21
|
// Easiest: start from @omega.js/desktop's default template.
|
|
22
22
|
tray.useDefaults();
|
|
@@ -25,7 +25,7 @@ module.exports = ({ manager, tray }) => {
|
|
|
25
25
|
tray.insertAfter('open', {
|
|
26
26
|
id: 'dashboard',
|
|
27
27
|
label: 'Open Dashboard',
|
|
28
|
-
click: () =>
|
|
28
|
+
click: () => omega.windows.show('dashboard'),
|
|
29
29
|
});
|
|
30
30
|
tray.update('open', { label: 'Show Window' });
|
|
31
31
|
tray.remove('website');
|
|
@@ -47,7 +47,7 @@ tray.clear() // start over
|
|
|
47
47
|
|
|
48
48
|
## Id-path API
|
|
49
49
|
|
|
50
|
-
Same shape across menu / tray / context-menu. Available **during definition** (on the `tray` builder arg) AND **at runtime** on `
|
|
50
|
+
Same shape across menu / tray / context-menu. Available **during definition** (on the `tray` builder arg) AND **at runtime** on `omega.tray`:
|
|
51
51
|
|
|
52
52
|
```js
|
|
53
53
|
.find(idPath) // live descriptor or null
|
|
@@ -69,8 +69,8 @@ Tray ids are **flat** — no `tray/` prefix (the lib namespace is implicit). For
|
|
|
69
69
|
| ID | Item |
|
|
70
70
|
|---|---|
|
|
71
71
|
| `title` | Disabled label showing the app name |
|
|
72
|
-
| `open` | "Open `<app>`"
|
|
73
|
-
| `check-for-updates` | Wired to `
|
|
72
|
+
| `open` | "Open `<app>`": calls `omega.windows.show('main')` |
|
|
73
|
+
| `check-for-updates` | Wired to `omega.autoUpdater` (label updates dynamically) |
|
|
74
74
|
| `website` | Visit `brand.url` (only present if configured) |
|
|
75
75
|
| `quit` | Quit the app |
|
|
76
76
|
|
|
@@ -84,9 +84,9 @@ tray.item({ id: 'account', label: 'Account', submenu: [
|
|
|
84
84
|
{ id: 'sign-out', label: 'Sign out', click: () => {} },
|
|
85
85
|
]});
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
omega.tray.find('account/sign-out');
|
|
88
|
+
omega.tray.update('account/sign-out', { enabled: false });
|
|
89
|
+
omega.tray.appendTo('account', { id: 'profile', label: 'Profile' });
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
## Item descriptors
|
|
@@ -103,30 +103,30 @@ Mirror Electron's [`MenuItemConstructorOptions`](https://www.electronjs.org/docs
|
|
|
103
103
|
| `click` | function | Wrapped to swallow errors so a bad handler can't kill the menu |
|
|
104
104
|
| `submenu` | array | Recursively resolved with the same conveniences |
|
|
105
105
|
|
|
106
|
-
## Runtime API on `
|
|
106
|
+
## Runtime API on `omega.tray`
|
|
107
107
|
|
|
108
108
|
```js
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
109
|
+
omega.tray.refresh() // re-evaluate dynamic state and re-render
|
|
110
|
+
omega.tray.define(fn) // replace the whole definition at runtime
|
|
111
|
+
omega.tray.disable() // tear down + stop responding (idempotent)
|
|
112
|
+
omega.tray.setIcon(path)
|
|
113
|
+
omega.tray.setTooltip(text)
|
|
114
|
+
omega.tray.addItem(descriptor) // append (preserves existing items)
|
|
115
|
+
omega.tray.clearItems()
|
|
116
|
+
omega.tray.destroy() // tear down (mostly for tests)
|
|
117
117
|
|
|
118
118
|
// Id-path API — same as listed above.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
119
|
+
omega.tray.find('quit')
|
|
120
|
+
omega.tray.update('quit', { label: 'Goodbye' })
|
|
121
|
+
omega.tray.remove('website')
|
|
122
|
+
omega.tray.insertAfter('open', { id: 'preferences', label: 'Preferences...', click: ... })
|
|
123
|
+
omega.tray.hide('check-for-updates')
|
|
124
124
|
|
|
125
125
|
// Inspection
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
126
|
+
omega.tray.getItems() // shallow copy of raw descriptors
|
|
127
|
+
omega.tray.getIcon()
|
|
128
|
+
omega.tray.getTooltip()
|
|
129
|
+
omega.tray.isRendered()
|
|
130
130
|
```
|
|
131
131
|
|
|
132
132
|
## Common patterns
|
|
@@ -135,8 +135,8 @@ manager.tray.isRendered()
|
|
|
135
135
|
|
|
136
136
|
```js
|
|
137
137
|
// in your renderer/main code, after sign-in:
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
omega.storage.set('user', { ... });
|
|
139
|
+
omega.tray.refresh(); // dynamic-label functions re-evaluate
|
|
140
140
|
```
|
|
141
141
|
|
|
142
142
|
### Hide updater item if you ship without auto-update
|
|
@@ -153,7 +153,7 @@ module.exports = ({ tray }) => {
|
|
|
153
153
|
### Replace the entire tray at runtime
|
|
154
154
|
|
|
155
155
|
```js
|
|
156
|
-
|
|
156
|
+
omega.tray.define(({ omega, tray }) => {
|
|
157
157
|
tray.icon('icons/dark-mode.png');
|
|
158
158
|
tray.item({ id: 'x', label: 'New layout', click: ... });
|
|
159
159
|
});
|
package/docs/usage.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# Usage
|
|
2
2
|
|
|
3
|
-
Tracks app-launch + hours-of-use stats. Sister of legacy @omega.js/desktop's Usage library, but uses `
|
|
3
|
+
Tracks app-launch + hours-of-use stats. Sister of legacy @omega.js/desktop's Usage library, but uses `omega.storage` instead of a separate electron-store.
|
|
4
4
|
|
|
5
5
|
## What's tracked
|
|
6
6
|
|
|
7
7
|
```js
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
omega.usage.opens() // total app launches
|
|
9
|
+
omega.usage.hoursTotal() // cumulative hours-of-use across clean exits
|
|
10
|
+
omega.usage.hoursThisSession() // live, computed from session start
|
|
11
|
+
omega.usage.installedAt() // ISO timestamp of first launch
|
|
12
|
+
omega.usage.toJSON() // all of the above as a structured-cloneable object
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## How it accumulates
|
|
@@ -51,7 +51,7 @@ const snap = await window.desktop.usage.get();
|
|
|
51
51
|
|
|
52
52
|
## Why not just use app-state?
|
|
53
53
|
|
|
54
|
-
`app-state.js` already tracks `launchCount` (= opens). We could fold these in. But `app-state` is concerned with first-launch / crash-sentinel / version-change semantics
|
|
54
|
+
`app-state.js` already tracks `launchCount` (= opens). We could fold these in. But `app-state` is concerned with first-launch / crash-sentinel / version-change semantics: `usage` is concerned with telemetry. Keeping them separate keeps each module focused. Both write to disjoint keys in `omega.storage`.
|
|
55
55
|
|
|
56
56
|
## Tests
|
|
57
57
|
|
package/docs/verts.md
CHANGED
|
@@ -12,7 +12,7 @@ zero consumer JS, same element vocabulary as the web `verts/unit` section and
|
|
|
12
12
|
|
|
13
13
|
## What the wiring does
|
|
14
14
|
|
|
15
|
-
`renderer.js _wireAds` (runs inside `
|
|
15
|
+
`renderer.js _wireAds` (runs inside `omega.initialize()`, same liveness
|
|
16
16
|
model as the FontAwesome/tooltip wiring) binds every `[data-omega-vert]`
|
|
17
17
|
element present at init AND inserted later (MutationObserver), marking bound
|
|
18
18
|
hosts `data-omega-vert-bound="house"`. Everything after the bind lives in the
|