@omega.js/desktop 0.53.0 → 0.54.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/README.md +38 -38
  2. package/dist/cli-run.js +4 -1
  3. package/dist/cli.js +2 -2
  4. package/dist/commands/cdp/client.js +1 -1
  5. package/dist/commands/cdp.js +1 -1
  6. package/dist/commands/clean.js +2 -3
  7. package/dist/commands/dev.js +25 -0
  8. package/dist/commands/lib/ensure-target.js +12 -17
  9. package/dist/commands/lib/migrate.js +17 -0
  10. package/dist/commands/logs.js +1 -1
  11. package/dist/commands/release.js +1 -1
  12. package/dist/commands/test.js +4 -4
  13. package/dist/commands/update.js +5 -4
  14. package/dist/defaults/.github/workflows/build.yml +18 -18
  15. package/dist/defaults/_.gitignore +0 -2
  16. package/dist/defaults/_mas/README.md +3 -3
  17. package/dist/defaults/config/certs/README.md +1 -1
  18. package/dist/defaults/config/omega.json5 +36 -36
  19. package/dist/defaults/docs/README.md +3 -3
  20. package/dist/defaults/gulpfile.js +1 -1
  21. package/dist/defaults/hooks/build/post.js +1 -1
  22. package/dist/defaults/hooks/build/pre.js +1 -1
  23. package/dist/defaults/hooks/notarize/post.js +2 -2
  24. package/dist/defaults/hooks/release/post.js +1 -1
  25. package/dist/defaults/hooks/release/pre.js +1 -1
  26. package/dist/defaults/src/assets/scss/pages/about.scss +1 -1
  27. package/dist/defaults/src/assets/scss/pages/main.scss +1 -1
  28. package/dist/defaults/src/assets/scss/pages/settings.scss +1 -1
  29. package/dist/defaults/src/integrations/context-menu/index.js +11 -11
  30. package/dist/defaults/src/integrations/menu/index.js +5 -5
  31. package/dist/defaults/src/integrations/tray/index.js +9 -9
  32. package/dist/defaults/src/main.js +2 -2
  33. package/dist/defaults/src/preload.js +1 -1
  34. package/dist/defaults/test/README.md +3 -3
  35. package/dist/defaults/test/_init.js +1 -1
  36. package/dist/gulp/tasks/audit.js +5 -8
  37. package/dist/lib/restart-manager/index.js +1 -1
  38. package/dist/lib/restart-manager/install.js +1 -1
  39. package/dist/lib/restart-manager/protocol.js +1 -1
  40. package/dist/main.js +4 -3
  41. package/dist/preload.js +1 -1
  42. package/dist/test/suites/build/audit.test.js +20 -7
  43. package/dist/test/suites/build/build-workflow-jobs.test.js +2 -2
  44. package/dist/test/suites/build/cli.test.js +28 -0
  45. package/dist/test/suites/build/defaults-em-dash.test.js +22 -0
  46. package/dist/test/suites/build/defaults-scaffold.test.js +19 -5
  47. package/dist/test/suites/build/deploy-direct.test.js +7 -5
  48. package/dist/test/suites/build/deploy-dispatch.test.js +2 -1
  49. package/dist/test/suites/build/deploy-hook.test.js +4 -2
  50. package/dist/test/suites/build/dev-verb.test.js +67 -0
  51. package/dist/test/suites/build/ensure-target.test.js +11 -3
  52. package/dist/test/suites/build/merge-line-files.test.js +6 -6
  53. package/dist/test/suites/build/migrate.test.js +29 -0
  54. package/dist/test/suites/build/project-scripts-deps.test.js +6 -10
  55. package/dist/test/suites/build/runner-env-write.test.js +73 -0
  56. package/dist/test/suites/build/runner.test.js +9 -8
  57. package/dist/test/suites/build/setup-scripts.test.js +27 -0
  58. package/dist/test/suites/build/validate-config.test.js +13 -2
  59. package/dist/test/suites/build/verb-logs.test.js +20 -0
  60. package/dist/test/suites/renderer/window-desktop-surface.test.js +1 -1
  61. package/dist/utils/build-pipeline.js +4 -4
  62. package/dist/utils/runner-env.js +13 -28
  63. package/dist/vendor/config/company.js +46 -14
  64. package/dist/vendor/config/defaults.js +30 -7
  65. package/dist/vendor/config/edit.js +25 -3
  66. package/dist/vendor/config/env-delivery.js +1 -1
  67. package/dist/vendor/config/env-schema.js +3 -6
  68. package/dist/vendor/config/env.js +34 -22
  69. package/dist/vendor/config/index.js +13 -17
  70. package/dist/vendor/config/load.js +15 -7
  71. package/dist/vendor/config/repo.js +10 -27
  72. package/dist/vendor/config/schema-client.js +64 -0
  73. package/dist/vendor/config/schema-cloud.js +38 -0
  74. package/dist/vendor/config/schema-manager.js +118 -0
  75. package/dist/vendor/config/schema-overrides.js +68 -0
  76. package/dist/vendor/config/schema.js +99 -152
  77. package/dist/vendor/config/validate.js +97 -77
  78. package/dist/vendor/devkit/agents-md.js +233 -0
  79. package/dist/vendor/devkit/attach-log-file.js +15 -1
  80. package/dist/vendor/devkit/ci-workflows.js +30 -30
  81. package/dist/vendor/devkit/cli-router.js +13 -7
  82. package/dist/vendor/devkit/defaults-engine.js +9 -43
  83. package/dist/vendor/devkit/deploy-snapshot.js +44 -9
  84. package/dist/vendor/devkit/env-lines.js +183 -0
  85. package/dist/vendor/devkit/local.js +62 -10
  86. package/dist/vendor/devkit/lockfile.js +32 -13
  87. package/dist/vendor/devkit/logger.js +7 -2
  88. package/dist/vendor/devkit/merge-line-files.js +219 -176
  89. package/dist/vendor/devkit/omega-bin.js +208 -111
  90. package/dist/vendor/devkit/preludes/docs-sync.js +52 -0
  91. package/dist/vendor/devkit/preludes/index.js +1 -0
  92. package/dist/vendor/devkit/target-picker.js +45 -0
  93. package/dist/vendor/devkit/test/dashed-files.js +37 -0
  94. package/dist/vendor/devkit/test/run-verb-under-tee.js +71 -0
  95. package/dist/vendor/devkit/update.js +15 -15
  96. package/dist/vendor/devkit/verb-scripts.js +40 -0
  97. package/dist/vendor/devkit/verbs.js +170 -0
  98. package/package.json +18 -24
  99. package/dist/commands/install.js +0 -37
  100. package/dist/defaults/AGENTS.md +0 -119
  101. package/dist/defaults/CLAUDE.md +0 -1
  102. package/dist/vendor/config/env-retired.js +0 -137
  103. package/dist/vendor/config/retired-keys.js +0 -635
  104. package/docs/analytics.md +0 -140
  105. package/docs/app-state.md +0 -92
  106. package/docs/audit.md +0 -69
  107. package/docs/auth.md +0 -284
  108. package/docs/auto-updater.md +0 -243
  109. package/docs/boot-sequence.md +0 -44
  110. package/docs/build-system.md +0 -169
  111. package/docs/cdp-debugging.md +0 -169
  112. package/docs/common-mistakes.md +0 -21
  113. package/docs/config-schema.md +0 -120
  114. package/docs/context-menu.md +0 -112
  115. package/docs/context.md +0 -81
  116. package/docs/css.md +0 -84
  117. package/docs/deep-link.md +0 -186
  118. package/docs/environment-detection.md +0 -112
  119. package/docs/fontawesome.md +0 -109
  120. package/docs/hooks.md +0 -89
  121. package/docs/icons.md +0 -79
  122. package/docs/index.md +0 -328
  123. package/docs/installer-options.md +0 -165
  124. package/docs/ipc.md +0 -61
  125. package/docs/lib-modules.md +0 -53
  126. package/docs/logging.md +0 -227
  127. package/docs/menu.md +0 -160
  128. package/docs/releasing.md +0 -239
  129. package/docs/remote-config.md +0 -118
  130. package/docs/remote-scripts.md +0 -144
  131. package/docs/restart-manager.md +0 -144
  132. package/docs/runner.md +0 -290
  133. package/docs/sentry.md +0 -97
  134. package/docs/shared/agent-docs.md +0 -89
  135. package/docs/shared/analytics.md +0 -612
  136. package/docs/shared/brands.md +0 -57
  137. package/docs/shared/breaking-changes.md +0 -917
  138. package/docs/shared/config.md +0 -1948
  139. package/docs/shared/deploys.md +0 -341
  140. package/docs/shared/icons.md +0 -219
  141. package/docs/shared/local-dev.md +0 -167
  142. package/docs/shared/logging.md +0 -205
  143. package/docs/shared/monitoring.md +0 -167
  144. package/docs/shared/publishing.md +0 -187
  145. package/docs/shared/rulings.md +0 -34
  146. package/docs/shared/testing.md +0 -147
  147. package/docs/shared/theming.md +0 -629
  148. package/docs/shared/translation.md +0 -342
  149. package/docs/shared/updates.md +0 -61
  150. package/docs/signing.md +0 -293
  151. package/docs/startup.md +0 -142
  152. package/docs/storage.md +0 -59
  153. package/docs/templating.md +0 -101
  154. package/docs/test-boot-layer.md +0 -157
  155. package/docs/test-framework.md +0 -362
  156. package/docs/themes.md +0 -149
  157. package/docs/tooltips.md +0 -99
  158. package/docs/tray.md +0 -164
  159. package/docs/usage.md +0 -58
  160. package/docs/verts.md +0 -62
  161. package/docs/windows.md +0 -149
@@ -1,21 +0,0 @@
1
- # Common Mistakes to Avoid
2
-
3
- 1. **Auto-creating windows in main.js**: @omega.js/desktop does NOT auto-create windows. The consumer's main.js must call `omega.windows.create('main', { show: !startup.isLaunchHidden() })` inside `omega.initialize().then()`. Always create `main`: even in hidden launches, with `show: false`, so the activate/second-instance handlers can surface UI on user re-launch.
4
- 2. **Putting JS logic in config** — Trays / menus / context-menus are file-based (`src/integrations/<name>/index.js`). Click handlers, dynamic labels, conditional visibility — all live in the JS file. Don't try to express them in `omega.json5`.
5
- 3. **Shipping `<slot>@2x.png` files** — Ship ONE file at the native (@2x) size; @omega.js/desktop downscales the @1x sibling. Bundled defaults work the same way. See [icons.md](icons.md).
6
- 4. **Naming the macOS tray icon source `trayTemplate.png`** — The input filename is `tray.png` (matches Windows/Linux). @omega.js/desktop owns the `Template` magic when writing to dist.
7
- 5. **Reading `process.cwd()` from packaged-app runtime code** — It's `/` in packaged apps. Use `require('./utils/app-root.js')()` (tries `app.getAppPath()` first, falls back for tests/non-Electron contexts).
8
- 6. **Setting `enabled: true` to turn on sentry/analytics** — Wrong convention. Set the credentials (`monitoring.providers.sentry.dsn = '...'`, `analytics.providers.google.id = '...'`); presence enables. Same for `cloud.config`.
9
- 7. **Defining a cross-context helper on one process's class alone**: write it as a plain function in `src/utils/<topic>-helpers.js` and call it from each process class (main, preload, renderer) and the build module, so they all share the same code path.
10
- 8. **Trying to share an `omega` instance across processes**: each process has its own. They communicate via IPC (`omega.ipc.invoke/handle` in main, `omega.desktop.ipc` in a renderer).
11
- 9. **Calling `shell.openExternal(url)` directly with a dynamic URL** — Gate through `require('./utils/sanitize-url.js')` first (returns `''` for non-http(s) protocols). Any dynamic URL must have its protocol filtered before navigation.
12
- 10. **Hand-editing `dist/electron-builder.yml` or `dist/config/entitlements.mac.plist`** — Both are generated by `gulp/build-config` from `config/omega.json5` + @omega.js/desktop defaults. Edit the source config; the YAML/plist regenerate every build.
13
- 11. **Forgetting that "build" failed but the .app launched anyway** — `ELECTRON_RUN_AS_NODE=1` makes Electron silently run as Node: `app` is undefined, no BrowserWindow, no window appears. The CLI boundary strips this var; if you see weird "nothing happens" launches in dev, check whether your shell has it set.
14
- 12. **Reading env vars ad-hoc in source**: use `omega.isDevelopment()`, `omega.isProduction()`, `omega.isTesting()`, `omega.getEnvironment()` instead. See [environment-detection.md](environment-detection.md).
15
- 13. **Installing @omega.js/desktop's dependencies as direct consumer deps**: consumer projects must NOT `npm install firebase`, `fs-jetpack`, `@omega.js/client`, or any other @omega.js/desktop/@omega.js/client transitive dep. The bundler re-resolves every name @omega.js/desktop DECLARES from the framework's own installation (`@omega.js/devkit/bundle`'s framework-deps hook). If a dependency isn't resolving, the fix is in @omega.js/desktop's `package.json` or its bundle task, not the consumer's `package.json`. Same rule on every OMEGA framework.
16
- 14. **Touching Firebase directly in consumer code**: Firebase is owned by @omega.js/client. Consumer code NEVER does `require('firebase')` or `import('firebase/app')`. In renderers use `omega.auth` and `omega.firestore` on the renderer instance. In the main process, use `omega.auth` (the @omega.js/desktop bridge). Same rule on every OMEGA browser surface.
17
- 15. **Forgetting `await` on `windows.create()`** — It's async and returns a Promise, not a BrowserWindow. Passing the Promise to code that calls `win.on(...)` silently fails with "is not a function".
18
- 16. **Using raw `import()` for ESM-only deps** — Use `importESM(specifier)` from `utils/import-esm.js`. It tries the consumer's `node_modules/` first, then falls back to @omega.js/desktop's own copy. This means consumers don't need to install @omega.js/desktop's transitive ESM deps (they're resolved from @omega.js/desktop's `node_modules/` automatically). A dep import the bundler cannot inline (a variable specifier) only resolves from the consumer — if the dep isn't installed there, it fails silently.
19
- 17. **Touching `process` in code shared with renderers** — With `contextIsolation: true` and `nodeIntegration: false` (the defaults), `process` does not exist in renderers — `process.platform` in a shared util throws a `ReferenceError`, it does not return `undefined`. Branch platform/env logic in main (or the preload) and hand the RESULT to the renderer (IPC, preload-exposed value, or a data attribute) — never share a util that dereferences `process` across contexts.
20
- 18. **Expecting `target="_blank"` to work in a `file://` renderer** — Anchors with `target="_blank"` silently do nothing; there is no browser to open. External links go through `shell.openExternal` (gated per mistake 9) — wire a click handler or use the framework's external-link binding rather than a bare anchor.
21
- 19. **Registering the brand-scheme handler on the default session only**: `protocol.handle('<brand.id>', handler)` covers ONLY the default session. Sessions from `session.fromPartition(...)` do NOT inherit it, and Electron auto-opens external protocols: a `brand://` load in a partitioned webContents is classified by Chromium as an EXTERNAL protocol and handed to the OS: macOS Launch Services then launches whatever installed app owns the scheme (an old production copy of YOUR app, mid-run). Silent in dev until it isn't. If your app uses partitions, register the same handler on every partition you vend, idempotently: e.g. a session-manager whose `getElectronSession()` does `ses.protocol.handle(scheme, handler)` guarded by a `WeakSet`. There is no Electron API to read a registered handler back, so this cannot be automated after the fact; register through one owned code path. (Candidate future @omega.js/desktop API: opt-in `omega.protocol.handle(handler)` that applies the handler to the default session + every `session-created`, see TODO.md.) The TEST harness already contains this class of escape during `mgr test` (stub brand handler + `openExternal` denied, see [test-framework.md](test-framework.md)), but that protects tests only, not your packaged app.
@@ -1,120 +0,0 @@
1
- # Config schema
2
-
3
- @omega.js/desktop validates `config/omega.json5` against the canonical OMEGA schema in **`@omega.js/config`** (vendored into `dist/vendor/config/` at prepare time; also exposed to consumers as `require('@omega.js/desktop/config')`). The shared schema covers the cross-framework sections (brand, cloud, analytics, payment, monitoring, connections, theme, targets); the desktop-specific refinements (app.category, the `platforms` shipping declaration, platforms.windows.signing.strategy, startup.mode, restartManager.*, …) live in the same package's `TARGET_SCHEMAS.desktop` and apply when validating with `{ target: 'desktop' }`. Validation always runs against the RESOLVED config: `targets.desktop` contents land at the top level (see the monorepo's `docs/shared/config.md` for the format).
4
-
5
- Validation runs in two places:
6
-
7
- 1. **`omega.initialize()` (boot, main)**: hard-fails the app at boot if any required field is missing or any present field is invalid. So a misconfigured app never reaches the "white window of confusion" phase: it tells you exactly which field is broken.
8
- 2. **`gulp audit` (build)**: same schema, plus build-pipeline-specific extras (file-existence for icons, an addressable releases repo in publish mode, etc.).
9
-
10
- ## Schema entry shape
11
-
12
- ```js
13
- {
14
- path: 'brand.id', // dot-path into the config
15
- type: 'string' | 'boolean' | 'number' | 'array' | 'object',
16
- required: true | false | (config) => bool,
17
- match: /^[a-z][a-z0-9+\-.]*$/, // string-value regex
18
- enum: ['normal', 'hidden'], // value-must-be-in-this-list
19
- description: 'Used for the deep-link scheme + default appId.',
20
- }
21
- ```
22
-
23
- ## The `required` flag
24
-
25
- @omega.js/desktop keeps validation simple: **`required` is either `true`, `false`, or a function**.
26
-
27
- ```js
28
- required: true // hard-fail if missing
29
- required: false // OK to omit (but if present, match/enum/type still run)
30
- required: (cfg) => bool // conditional — predicate gets the full config
31
- ```
32
-
33
- The function form is for "this field is mandatory only when another part of config is set." Illustrative shape (no current entry uses it — every present-day field is `true` or `false`):
34
-
35
- ```js
36
- {
37
- path: 'analytics.providers.google.id',
38
- required: (cfg) => Boolean(cfg?.analytics?.providers?.google?.secret), // id mandatory only when a secret is configured
39
- match: /^G-[A-Z0-9]+$/,
40
- }
41
- ```
42
-
43
- This is identical strictness in dev and production. There's no separate `'publish-only'` tier — if a field truly matters only for builds, validate it inside `gulp/audit.js` (next to `fileMustExist` calls for icons, etc.) rather than the schema.
44
-
45
- ## How `match` / `enum` / `type` interact with absence
46
-
47
- They **only run when the value is present**. A missing field with `required: false` is silent. A missing field with `required: true` fires the "missing" error and nothing else — so consumers don't see a confusing flood of "missing AND wrong type AND doesn't match" for the same field.
48
-
49
- ## Presence-driven feature flags (@omega.js/backend convention)
50
-
51
- A non-empty credential value enables a feature — there is no separate `enabled: true/false` flag for credential-gated features:
52
-
53
- | Feature | Enable signal | Disable signal |
54
- |---|---|---|
55
- | Sentry | `monitoring.providers.sentry.dsn = 'https://...'` | `monitoring.providers.sentry.dsn = ''` |
56
- | GA4 analytics | `analytics.providers.google.id = 'G-XXXXX'` | `analytics.providers.google.id = ''` |
57
- | Firebase Auth (renderer) | `cloud.config.projectId = '...'` (etc.) | empty `cloud.config` |
58
-
59
- **Exceptions where an explicit `enabled` flag exists:** `remoteConfig.enabled`, `autoUpdate.enabled`, `releases.enabled`, `restartManager.enabled`, `startup.openAtLogin.enabled`. (`platforms.linux.snap.enabled` was one until [#867](https://github.com/Omega-JS-Stack/omega/issues/867): the snap is a declared FORMAT now, so its presence is the switch and `platforms.linux.formats.snap: false` is the off.) These toggle BEHAVIOR, not credentials: a fork can keep the brand's `repo.org` and still want releases off, for example.
60
-
61
- ## Adding a new field
62
-
63
- When you add a new config knob anywhere in @omega.js/desktop:
64
-
65
- 1. Add an entry to `TARGET_SCHEMAS.desktop` in `@omega.js/config` (`packages/config/src/schema.js` in the Omega monorepo) — or to `SHARED_SCHEMA` if the field is genuinely cross-framework.
66
- 2. If it has a default, set it in [`src/defaults/config/omega.json5`](../src/defaults/config/omega.json5) (under `targets.desktop` for desktop-scoped fields).
67
- 3. That's it. No separate validation logic to add elsewhere — the schema entry is the validation.
68
-
69
- ## What's NOT in the schema
70
-
71
- These checks live in [`gulp/tasks/audit.js`](../src/gulp/tasks/audit.js) instead, because they depend on build-pipeline state rather than the config shape:
72
-
73
- - **`src/main.js` / `src/preload.js` existence** — the bundle task skips them with a warning but the schema doesn't know about consumer entry points.
74
- - **`brand.images.icon` file existence** — only enforced when packaging (`isBuildMode()` / `isPublishMode()`); dev runs with the default Electron icon.
75
- - **An addressable releases repo** (`repo.org` + `brand.id`), only enforced in publish mode.
76
-
77
- These are kept in `audit.js` so the schema stays a pure description of the config shape, callable from any context without dragging in build state.
78
-
79
- ## Examples
80
-
81
- Required field missing:
82
-
83
- ```
84
- @omega.js/desktop: config validation failed — fix the following in config/omega.json5:
85
- 1. config.brand.id is required — URL-scheme-safe slug. Used as deep-link scheme + default appId. Must be lowercase, start with a letter, alnum/+/-/.
86
- ```
87
-
88
- Field present but invalid:
89
-
90
- ```
91
- 1. config.startup.mode "tray-only" is not allowed — must be one of [normal, hidden]
92
- 2. config.brand.id "My App!" does not match expected pattern /^[a-z][a-z0-9+\-.]*$/ — URL-scheme-safe slug. Used as deep-link scheme + default appId. Must be lowercase, start with a letter, alnum/+/-/.
93
- ```
94
-
95
- Errors are numbered so you can fix everything in one pass instead of fix-rebuild-fix-rebuild.
96
-
97
- ## Adding payment fields (@omega.js/backend-shaped)
98
-
99
- @omega.js/desktop's schema mirrors [@omega.js/backend's `manager-config.example.json`](https://github.com/itw-creative-works/backend-manager) shape for payment so the same product catalog reads identically on backend, web, and desktop:
100
-
101
- ```js
102
- {
103
- payment: {
104
- providers: {
105
- stripe: { publishableKey: 'pk_live_...' }, // schema: match /^pk_(test|live)_/
106
- paypal: { clientId: '...' },
107
- },
108
- products: [
109
- { id: 'basic', name: 'Basic', type: 'subscription', limits: { credits: 100 } },
110
- ],
111
- },
112
- }
113
- ```
114
-
115
- The schema only enforces shape for the few well-defined publishable keys — the product catalog itself is freeform so @omega.js/backend can extend it without @omega.js/desktop caring.
116
-
117
- ## Source
118
-
119
- - Schema definitions + validator engine: `@omega.js/config` (`packages/config/src/{schema,validate}.js` in the Omega monorepo; vendored copy at `dist/vendor/config/`)
120
- - @omega.js/desktop integration tests: [`src/test/suites/build/validate-config.test.js`](../src/test/suites/build/validate-config.test.js)
@@ -1,112 +0,0 @@
1
- # Context Menu (Right-Click)
2
-
3
- File-based context menu. Unlike tray and application menu (called once at boot), the context-menu definition is called **every time the user right-clicks** — so it gets fresh `params` each time and can vary the menu by selection.
4
-
5
- ## Config
6
-
7
- No config block. Path is conventional: `src/integrations/context-menu/index.js`. To opt out, call `omega.contextMenu.disable()` from your main entry: after that, right-click events are silently swallowed.
8
-
9
- ## Definition file
10
-
11
- ```js
12
- // src/integrations/context-menu/index.js
13
- module.exports = ({ omega, menu, params, webContents }) => {
14
- // Easiest: start from @omega.js/desktop's defaults, then customize per event.
15
- menu.useDefaults();
16
-
17
- // Add a "Search Google" entry when text is selected:
18
- if (params.selectionText) {
19
- menu.insertAfter('copy', {
20
- id: 'search-google',
21
- label: `Search "${params.selectionText.slice(0, 20)}"`,
22
- click: () => require('electron').shell.openExternal(
23
- `https://google.com/search?q=${encodeURIComponent(params.selectionText)}`,
24
- ),
25
- });
26
- }
27
-
28
- // Hide the dev-tools entries even in development:
29
- menu.remove('toggle-devtools');
30
- };
31
- ```
32
-
33
- Calling no `menu.*` methods (or `menu.clear()` after `useDefaults()` with nothing added) **suppresses the popup** entirely.
34
-
35
- ## Builder API (per event)
36
-
37
- ```js
38
- menu.item(descriptor)
39
- menu.separator()
40
- menu.submenu(label, items)
41
- menu.useDefaults() // populate with @omega.js/desktop's defaults based on params
42
- menu.clear() // wipe items added so far this event
43
- ```
44
-
45
- ## Id-path API (per event)
46
-
47
- Same shape across menu / tray / context-menu. Available **inside the definition fn** on the `menu` builder. Operates on the items being built for the current right-click event:
48
-
49
- ```js
50
- .find(idPath)
51
- .has(idPath)
52
- .update(idPath, patch)
53
- .remove(idPath)
54
- .enable(idPath, bool = true)
55
- .show(idPath, bool = true)
56
- .hide(idPath)
57
- .insertBefore(idPath, item)
58
- .insertAfter(idPath, item)
59
- .appendTo(idPath, item)
60
- ```
61
-
62
- Context-menu ids are **flat** — no `context/` prefix needed (the lib namespace is implicit). Submenus you build with `menu.submenu(...)` are addressable as `parent/child` paths via the resolver.
63
-
64
- (Runtime-on-`omega.contextMenu` mutators don't apply here: items are rebuilt every event. Mutate inside the definition fn instead.)
65
-
66
- ## Default template ids
67
-
68
- @omega.js/desktop's `useDefaults()` populates items based on `params`. Every default item carries an id you can target:
69
-
70
- | ID | When it appears |
71
- |---|---|
72
- | `undo`, `redo` | `params.editFlags.canUndo` / `canRedo` |
73
- | `cut`, `copy`, `paste`, `paste-and-match-style`, `select-all` | `params.isEditable` |
74
- | `copy` | `params.selectionText` (read-only) |
75
- | `open-link`, `copy-link` | `params.linkURL` |
76
- | `reload` | always |
77
- | `inspect`, `toggle-devtools` | `omega.isDevelopment()` only |
78
-
79
- ## Definition fn arguments
80
-
81
- | Arg | Description |
82
- |---|---|
83
- | `omega` | The running @omega.js/desktop main-process instance |
84
- | `menu` | Per-event builder + id-path API |
85
- | `params` | Electron's [`ContextMenuParams`](https://www.electronjs.org/docs/latest/api/web-contents#event-context-menu) — `selectionText`, `isEditable`, `linkURL`, `srcURL`, `mediaType`, `editFlags`, `x`, `y`, etc. |
86
- | `webContents` | The `webContents` that fired the event |
87
-
88
- ## Auto-attach
89
-
90
- Every window created via `omega.windows.createNamed()` is automatically wired up with the context-menu listener. Idempotent per `webContents` (uses a `WeakSet`). For windows you create directly with `new BrowserWindow()`, call:
91
-
92
- ```js
93
- omega.contextMenu.attach(win.webContents);
94
- ```
95
-
96
- ## Runtime API on `omega.contextMenu`
97
-
98
- ```js
99
- omega.contextMenu.define(fn) // replace the definition at runtime
100
- omega.contextMenu.disable() // ignore future right-click events (idempotent)
101
- omega.contextMenu.attach(webContents) // manual attach
102
- omega.contextMenu.buildItems(params, wc) // run the definition without popping a menu (useful for tests)
103
- omega.contextMenu.hasCustomDefinition() // false → using the built-in default fn
104
- ```
105
-
106
- ## Default fn
107
-
108
- Without a consumer file, @omega.js/desktop uses a built-in fallback that just calls `useDefaults()` — sensible undo/redo/cut/copy/paste/link/reload/inspect baseline. Same behavior as the default scaffold.
109
-
110
- ## Default scaffold
111
-
112
- The scaffold every verb runs ships `src/integrations/context-menu/index.js` calling `menu.useDefaults()` plus commented-out examples covering insertAfter, remove, hide, enable, and building from scratch.
package/docs/context.md DELETED
@@ -1,81 +0,0 @@
1
- # Context
2
-
3
- Runtime info block. Mirrors @omega.js/backend's `assistant.request.{geolocation,client}` shape so @omega.js/desktop apps + sister projects (@omega.js/backend, UJM, @omega.js/client) all reference the same property paths when reading user info.
4
-
5
- Populated asynchronously during `omega.initialize()`.
6
-
7
- ## Shape
8
-
9
- ```js
10
- omega.context.geolocation = {
11
- ip: '203.0.113.42', // async-fetched via ipify
12
- country: null, // future enhancement
13
- region: null,
14
- city: null,
15
- };
16
-
17
- omega.context.client = {
18
- userAgent: 'Mozilla/5.0 ...', // app.userAgentFallback
19
- locale: 'en-US', // app.getLocale()
20
- platform: 'darwin', // os.platform()
21
- arch: 'arm64', // os.arch()
22
- mobile: false, // always false on @omega.js/desktop (desktop framework)
23
- };
24
-
25
- omega.context.session = {
26
- id: '<uuid>', // fresh per launch (crypto.randomUUID)
27
- startTime: '2026-05-08T...', // ISO at boot
28
- deviceId: '<uuid or MAC>', // stable per-machine
29
- };
30
-
31
- omega.context.app = {
32
- version: '1.2.3', // omega.getVersion()
33
- environment: 'production', // omega.getEnvironment()
34
- isPackaged: true, // app.isPackaged
35
- };
36
- ```
37
-
38
- ## Device ID resolution
39
-
40
- The walk itself is the shared one — `@omega.js/analytics`' `deriveDeviceId({ get, set, seed })`, the same call `@omega.js/client` makes on a page ([#396](https://github.com/Omega-JS-Stack/omega/issues/396)). What this module supplies is desktop's own world: electron-store, and the MAC as the seed. Order:
41
-
42
- 1. **Storage** — already persisted from a prior boot. Wins so we're stable across NIC swaps / VPN changes.
43
- 2. **First non-internal MAC** from `os.networkInterfaces()`, the injected seed. Stable on a stable rig, and it hands a reinstalled app the id it had before its storage was wiped.
44
- 3. **A generated UUID** — the shared derivation's floor. Persisted on first launch.
45
-
46
- Once resolved on first launch it never changes. This is the input to `analytics._clientId = uuidv5(deviceId, projectIdNamespace)`, and it is desktop's alone: a browser on the same machine derives its own id from its own localStorage.
47
-
48
- ## Geolocation
49
-
50
- `geolocation.ip` is fetched in the background via `https://api.ipify.org?format=json`. Cached to `storage.context.geolocation` so the next launch has last-known-good values even if offline. The `country/region/city` fields are reserved for a future enrichment provider.
51
-
52
- Failure mode: a failed ipify fetch leaves the previous cached value untouched. The app keeps working with last-known-good.
53
-
54
- ## API
55
-
56
- ```js
57
- omega.context.geolocation.ip // direct read
58
- omega.context.session.deviceId // direct read
59
- const snap = omega.context.toJSON(); // structured-cloneable snapshot
60
- ```
61
-
62
- Renderer:
63
-
64
- ```js
65
- const snap = await window.desktop.context.get();
66
- console.log(snap.session.deviceId);
67
- ```
68
-
69
- ## Why the @omega.js/backend shape
70
-
71
- Sister projects (@omega.js/backend, @omega.js/client, UJM) all reference paths like `assistant.request.geolocation.country` and `assistant.request.client.userAgent`. @omega.js/desktop matches the leaf names so consumer code can write logic that works across all four runtimes:
72
-
73
- ```js
74
- const country = omega.context.geolocation.country
75
- || assistant.request.geolocation.country // @omega.js/backend
76
- || omega.context.geolocation.country;
77
- ```
78
-
79
- ## Tests
80
-
81
- - `src/test/suites/main/context.test.js` — session shape, deviceId stability across re-init, the injected seed + persistence of the shared derivation, client info, IPC handler, JSON-roundtrippability.
package/docs/css.md DELETED
@@ -1,84 +0,0 @@
1
- # CSS Architecture
2
-
3
- @omega.js/desktop styles are SCSS, compiled by the pipeline's `sass` task into per-window bundles on top of a shared base. Bootstrap 5 (via @omega.js/desktop's classy theme) is the foundation — consumers restyle Bootstrap, they don't replace it.
4
-
5
- ## Main entry
6
-
7
- `<consumer>/src/assets/scss/main.scss` — loaded by EVERY window. It configures the theme via `@use ... with (...)`:
8
-
9
- ```scss
10
- // Generated from `brand.color` by the sass task (#912).
11
- @use 'brand';
12
-
13
- @use 'omega-desktop' as * with (
14
- $primary: brand.$primary,
15
- $dark: #1a1a2e,
16
- $classy-bg-dark: #0f0f1a,
17
- $classy-bg-dark-secondary: #161628,
18
- $classy-bg-dark-tertiary: #1e1e38,
19
- );
20
-
21
- // The runtime --omega-accent ramp, after the framework import.
22
- @include brand.ramp;
23
-
24
- // Custom global styles below
25
- ```
26
-
27
- Compiles to `dist/assets/css/main.bundle.css` (Bootstrap + classy theme + your globals).
28
-
29
- ## Per-window styles
30
-
31
- `src/assets/scss/pages/<window>.scss` → `dist/assets/css/components/<window>.bundle.css`, loaded ONLY on that window's page. One file per window (`main.scss`, `settings.scss`, …) — page-specific chrome lives here, shared styles live in the main entry.
32
-
33
- ## Theme integration
34
-
35
- The `@use 'omega-desktop'` entry pulls in Bootstrap 5 + @omega.js/desktop's classy theme. Appearance (`system`/`light`/`dark`) defaults from `config.theme.appearance` and is applied + kept live on `<html data-bs-theme>` by `omega.theme` (OS-following, runtime-switchable, persisted override, see [themes.md](themes.md)). Theme variables (`$primary`, `$dark`, `$classy-bg-*`, typography, borders) are overridable via the `with (...)` block, and `$primary` arrives from `brand.color` through the generated `dist/assets/scss/_brand.scss` unless a literal replaces `brand.$primary` ([#912](https://github.com/Omega-JS-Stack/omega/issues/912)). See [themes.md](themes.md) for the full variable reference.
36
-
37
- ## Icon presentation
38
-
39
- Icon CSS is ONE sheet for every omega target, vendored from @omega.js/web at prepare (package.json `omega.vendorAssets` → `dist/assets/css/core/_fontawesome.scss`) and loaded by the `omega-desktop` entry. It ships the square glyph-centered box every rendered `<i>` gets, the `fa-2xs`…`fa-6xl` size scale, and the `fa-spin` / `fa-bounce` / `fa-beat` utilities (each parked under `prefers-reduced-motion`). Nothing to import and nothing to hand-fix. See [shared/icons.md](shared/icons.md).
40
-
41
- ## App shell
42
-
43
- Dashboard/admin windows use the `.omega-shell` layout — a sidebar + topbar + main grid with a collapsible desktop rail and a mobile drawer. Two layers ship, both vendored from @omega.js/web at prepare: the MECHANICS (`dist/assets/css/shell/_index.scss` — the grid, region geometry, states, and the `--omega-shell-*` tokens, loaded by the `omega-desktop` entry before the theme) and the theme's SKIN (`dist/assets/themes/<theme-id>/css/layout/_shell.scss`, layered over it). Nothing to import — `@use 'omega-desktop'` gets both.
44
-
45
- Emit this markup in the window's HTML:
46
-
47
- ```html
48
- <div class="omega-shell" data-omega-shell>
49
- <aside class="omega-shell__sidebar" id="app-sidebar">
50
- <!-- pinned head (brand, selector) sits here, outside the scroll region -->
51
- <div class="omega-shell__sidebar-scroll">
52
- <!-- nav scrolls HERE (the rail itself clips nothing, so popovers can
53
- escape); text that should hide in the collapsed rail wears
54
- .omega-shell__label -->
55
- </div>
56
- </aside>
57
-
58
- <header class="omega-shell__topbar">
59
- <div class="omega-shell__topbar-start">
60
- <button data-shell-toggle="drawer" aria-expanded="false" aria-controls="app-sidebar">☰</button>
61
- <button data-shell-toggle="collapse" aria-expanded="true" aria-controls="app-sidebar">⇤</button>
62
- </div>
63
- <div class="omega-shell__topbar-end"><!-- account menu, actions --></div>
64
- </header>
65
-
66
- <main class="omega-shell__main"><!-- page content --></main>
67
-
68
- <div class="omega-shell__scrim" data-shell-dismiss></div>
69
- </div>
70
- ```
71
-
72
- The renderer instance wires the behavior during `omega.initialize()` (the vendored `__main_assets__/js/core/app-shell.js`, the same module @omega.js/web runs), so a view that renders the markup needs no script of its own. The API is `omega.shell` (`isCollapsed`, `isOpen`, `setCollapsed`, `setOpen`, `toggleCollapsed`, `toggleOpen`).
73
-
74
- The module is delegated and declarative: `[data-shell-toggle="collapse"]` toggles the rail, `[data-shell-toggle="drawer"]` toggles the mobile drawer, `[data-shell-dismiss]` (and Escape) closes it. It stamps the state on the container: `data-shell-collapsed="true"` (persisted under the `shell.collapsed` storage key) and `data-shell-open="true"`, which is what the CSS keys off. Add `.omega-shell--locked` when `main` should never scroll (the page manages its own interior scroll).
75
-
76
- ## Bootstrap-first convention
77
-
78
- NEVER create custom classes for things Bootstrap already provides — use `btn`, `card`, `form-*`, `d-flex`, `gap-*`, `rounded-*`, `bg-body-*`, `text-*` natively, and use `bg-body` variants (not `bg-light`/`bg-dark`) so dark mode adapts. Theme SCSS overrides how Bootstrap components LOOK; custom CSS is only for genuinely novel components with no Bootstrap equivalent. Same rule in BXM and UJM.
79
-
80
- ## See also
81
-
82
- - [themes.md](themes.md) — theme variables, appearance modes
83
- - [build-system.md](build-system.md) — where the `sass` task runs in the pipeline
84
- - [templating.md](templating.md) — the page template that loads the bundles
package/docs/deep-link.md DELETED
@@ -1,186 +0,0 @@
1
- # Deep Links
2
-
3
- Cross-platform deep-link handling that's simple to use and hard to get wrong. @omega.js/desktop owns all the OS plumbing (single-instance lock, scheme registration, argv parsing, second-instance routing, focus-on-warm-start) and gives you one unified event API regardless of how the link arrived.
4
-
5
- ## Config
6
-
7
- ```jsonc
8
- "deepLinks": {
9
- "schemes": ["myapp"] // urls like myapp://...
10
- }
11
- ```
12
-
13
- @omega.js/desktop registers each scheme with the OS via `app.setAsDefaultProtocolClient` so the system routes matching URLs to your app. Registration is **production-only**: dev/test runs never claim OS-wide protocol handlers for unpackaged Electron binaries. In dev, exercise your handlers with `omega.deepLink.dispatch(url)` instead.
14
-
15
- ## How it works (so you don't have to think about it)
16
-
17
- | Platform | Cold-start (app not running) | Warm-start (app already running) |
18
- |---|---|---|
19
- | **macOS** | `app.on('open-url')` — queued before `whenReady`, drained after | `app.on('open-url')` |
20
- | **Windows** | URL appended to `process.argv`; @omega.js/desktop extracts it | OS forwards argv to the existing instance via `app.on('second-instance')`; @omega.js/desktop reads the duplicate's real argv from that event's `additionalData` |
21
- | **Linux** | Same as Windows | Same as Windows |
22
-
23
- @omega.js/desktop handles all of these and dispatches them through the same `omega.deepLink.on()` event registry. Your code looks identical regardless of platform or cold/warm start. Single-instance lock is acquired automatically (via `lib/protocol.js`); duplicate launches exit cleanly and forward their argv to the original instance.
24
-
25
- ## Public API
26
-
27
- ```js
28
- omega.deepLink.on(pattern, handler) // register a handler. Returns unsubscribe fn.
29
- omega.deepLink.off(pattern, handler)
30
- omega.deepLink.dispatch(url) // manually fire (testing, custom triggers)
31
- omega.deepLink.getColdStartUrl() // the URL the app was launched with, or null
32
- ```
33
-
34
- ## Patterns
35
-
36
- ```
37
- 'auth/token' // exact match
38
- 'user/profile/:id' // named param → ctx.params.id
39
- 'org/:slug/repo/:repo' // multiple params
40
- '*' // wildcard catch-all (only fires when no concrete handler matched)
41
- ```
42
-
43
- ## Handler signature
44
-
45
- ```js
46
- omega.deepLink.on('user/profile/:id', (ctx) => {
47
- ctx.url // 'myapp://user/profile/42?ref=tray'
48
- ctx.scheme // 'myapp'
49
- ctx.route // 'user/profile/42'
50
- ctx.pattern // 'user/profile/:id'
51
- ctx.params // { id: '42' }
52
- ctx.query // { ref: 'tray' }
53
- ctx.source // 'cold-start' | 'warm-start' | 'manual'
54
- ctx.argv // process.argv (cold) or the duplicate's real argv (warm, from additionalData)
55
- ctx.cwd // working directory (the duplicate's on warm-start)
56
- ctx.handled // mutable: set true to suppress remaining handlers (including built-ins)
57
- });
58
- ```
59
-
60
- ## Built-in routes
61
-
62
- @omega.js/desktop ships with handlers for common patterns. They run AFTER consumer handlers, so you can shadow any of them by registering your own handler at the same pattern.
63
-
64
- | Route | Default behavior |
65
- |---|---|
66
- | `auth/token` | Calls `omega.auth.handleToken(query.authToken)`: the receiving end of the `omega.openAuthFlow()` sign-in round-trip. MODERN shape only (`?authToken=`, what the website's token page sends); other formats (`?token=`, `?payload=`) are ignored. In production the URL arrives via the OS scheme; in dev/test `lib/auth-flow.js`'s loopback listener dispatches the same URL manually (the scheme isn't OS-registered in dev: protocol.js registers only in production, and macOS can't runtime-register unlisted schemes at all) |
67
- | `app/show` | `omega.windows.show(query.window || 'main')` |
68
- | `app/quit` | `app.quit()` |
69
-
70
- ### Overriding a built-in
71
-
72
- ```js
73
- // Replace the built-in app/show with custom logic.
74
- omega.deepLink.on('app/show', (ctx) => {
75
- if (ctx.query.window === 'admin' && !omega.appState.isAdminUser()) {
76
- showError('not authorized');
77
- ctx.handled = true; // suppress built-in
78
- return;
79
- }
80
- // Otherwise let the built-in run normally.
81
- });
82
- ```
83
-
84
- ## Resolution order
85
-
86
- For each incoming URL, @omega.js/desktop walks handlers in this order:
87
-
88
- 1. **Consumer concrete handlers** (any non-wildcard pattern you registered with `.on()`)
89
- 2. **Built-in concrete handlers** (`auth/token`, `app/show`, `app/quit`)
90
- 3. **Wildcard handlers** (`'*'`) — only if NO concrete handler matched
91
-
92
- Setting `ctx.handled = true` in any handler stops the cascade. Within a single tier, handlers fire in registration order. Errors in a handler are caught and logged — they don't stop subsequent handlers.
93
-
94
- ## Common patterns
95
-
96
- ### Route to a window + send IPC
97
-
98
- ```js
99
- omega.deepLink.on('user/profile/:id', (ctx) => {
100
- omega.windows.show('main');
101
- omega.windows.get('main').webContents.send('navigate', {
102
- to: `/profile/${ctx.params.id}`,
103
- });
104
- });
105
- ```
106
-
107
- ### Catch-all logger
108
-
109
- ```js
110
- omega.deepLink.on('*', (ctx) => {
111
- omega.logger.warn(`Unrouted deep link: ${ctx.url}`);
112
- });
113
- ```
114
-
115
- ### Cold-start branching
116
-
117
- ```js
118
- const coldUrl = omega.deepLink.getColdStartUrl();
119
- if (coldUrl) {
120
- omega.logger.log(`Launched from deep link: ${coldUrl}`);
121
- // appState.launchedFromDeepLink() is also set automatically
122
- }
123
- ```
124
-
125
- ### Manually dispatching (e.g. from a tray click)
126
-
127
- ```js
128
- tray.item({
129
- label: 'Open Profile',
130
- click: () => omega.deepLink.dispatch('myapp://user/profile/me'),
131
- });
132
- ```
133
-
134
- ## Boot queueing
135
-
136
- Every dispatch is held until `omega.initialize()` completes (main.js calls `deepLink.markOmegaReady()` as its last step). A cold-start `auth/token` link (the OS launching the app from the sign-in round trip) therefore never fires before `omega.auth` has Firebase up; it queues and drains the moment the instance is ready. Warm-start dispatches on a running app pass straight through.
137
-
138
- ## Single-instance behavior
139
-
140
- @omega.js/desktop acquires the OS-level single-instance lock during `protocol.initialize()` (boot step 5, before deep-link inits). If another copy of the app is already running:
141
-
142
- 1. The new instance loses the lock.
143
- 2. The OS forwards its argv to the original instance.
144
- 3. The new instance's `omega.initialize()` halts (after `protocol.hasSingleInstanceLock() === false`): the duplicate quits and its promise never settles.
145
- 4. The original instance's `app.on('second-instance')` fires with the Chromium-processed argv as its second argument AND the duplicate's real argv as its fourth, `additionalData` (@omega.js/desktop passes `{ argv, cwd }` to `app.requestSingleInstanceLock()` for you).
146
- 5. @omega.js/desktop extracts the deep-link URL from that argv and dispatches normally — but as `source: 'warm-start'`.
147
- 6. @omega.js/desktop also focuses the existing main window automatically (consumer can override by registering a route handler that does its own thing).
148
-
149
- Reading the duplicate's own flags (a CLI-shaped app, a `--open <file>` handler) means reading that fourth argument:
150
-
151
- ```js
152
- app.on('second-instance', (event, argv, cwd, additionalData) => additionalData.argv);
153
- ```
154
-
155
- Never parse the event's own `argv` for flags: Chromium re-serializes it (switches first, Chromium's own switches spliced in, the values detached at the end), so a `--message two` launch arrives with the value detached from the flag.
156
-
157
- ## Linking with `appState`
158
-
159
- When a deep link is detected at cold-start, @omega.js/desktop calls `omega.appState.setLaunchedFromDeepLink(true)`. This means:
160
-
161
- ```js
162
- if (omega.appState.launchedFromDeepLink()) {
163
- // user clicked a link to launch the app — handle differently than a tray click or login launch
164
- }
165
- ```
166
-
167
- Combine with `appState.isFirstLaunch()` to detect "first launch via deep link" (e.g. from an onboarding flow on your website).
168
-
169
- ## Testing
170
-
171
- The dispatch pipeline is unit-testable without actually triggering an OS event:
172
-
173
- ```js
174
- omega.deepLink.dispatch('myapp://auth/token?token=test');
175
- // Fires source='manual'. Handlers run synchronously.
176
- ```
177
-
178
- See `src/test/suites/main/deep-link.test.js` for the full coverage.
179
-
180
- ## Implementation notes
181
-
182
- - `lib/protocol.js` owns the single-instance lock + scheme registration; `lib/deep-link.js` owns the dispatch pipeline. They're separate modules but tightly coupled.
183
- - OS scheme registration is gated on `omega.isProduction()` (in `lib/protocol.js`): unpackaged dev/test binaries are never registered as system protocol handlers (unconditional registration also intermittently triggered macOS Launch Services `-600` dialogs during test runs).
184
- - On Windows/Linux, scheme registration uses `app.setAsDefaultProtocolClient(scheme, process.execPath, [process.cwd()])` so `app.exe scheme://...` style invocations route argv correctly.
185
- - macOS open-url events that arrive before `whenReady` are queued internally and drained on `deepLink.initialize()`.
186
- - Argv extraction walks backward from the end of argv (where the URL typically sits) and matches against registered schemes.