summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-client/src
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-11 14:17:55 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-11 17:51:51 +0200
commitd4935aa2a28fcbab8c3556e3532e53667092701e (patch)
tree2898874e80493bb7067401b4462b51edba267e69 /packages/meshbay-client/src
parentbca6fc3f0884fcdb0455b502ee4495b04945baee (diff)
downloadmeshbay-d4935aa2a28fcbab8c3556e3532e53667092701e.tar.gz
feat: gate the create-group wizard on whether a node is bundled
MeshBay Light has no bundled meshbay-node.exe, so the create-group wizard (which assumes it can start a local node) needs its own signal, not just platform.node.available. main.js exposes it over IPC (node:bundled) by checking the packaged resources directory rather than trusting a build-time constant; preload.js and platform.js carry it through the usual contextBridge/wrapper path. winCanElevateServiceMode() replaces the two prior 'app.isPackaged' checks for whether the app can offer service-mode elevation -- Light is packaged but has no service-mode.ps1 to elevate into, so packaged alone was already the wrong test even before this target existed. create-group-page.js gates the wizard step that starts a node on the new capability instead of hiding the whole feature; node-page.js's comment fix is unrelated cosmetic drift caught in the same pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-client/src')
-rw-r--r--packages/meshbay-client/src/main.js50
-rw-r--r--packages/meshbay-client/src/preload.js4
2 files changed, 47 insertions, 7 deletions
diff --git a/packages/meshbay-client/src/main.js b/packages/meshbay-client/src/main.js
index 83cf772..d7e3e7b 100644
--- a/packages/meshbay-client/src/main.js
+++ b/packages/meshbay-client/src/main.js
@@ -984,6 +984,22 @@ function registerBridge() {
}
});
+ // Does THIS build ship its own node, as opposed to one merely reachable on
+ // PATH (findNodeBinary's fallback, meant for a dev venv -- not "this
+ // installer provisioned a node"). The "Light" target (electron-builder
+ // .light.yml) ships no node-runtime extraResource at all; the renderer
+ // uses this to fall back to the browser-only "create a group" form
+ // instead of the wizard that assumes a local node it can link right there
+ // (create-group-page.js) -- see C:\Users\admin\devel\light-client.md 5.3.
+ // Unaffected off win32 and in dev: only a packaged Windows build can even
+ // be Light, so everything else keeps today's behaviour unconditionally.
+ function hasBundledNode() {
+ if (process.platform === 'win32' && app.isPackaged) {
+ return fs.existsSync(path.join(process.resourcesPath, 'node-runtime', 'meshbay-node.exe'));
+ }
+ return true;
+ }
+
function findNodeBinary() {
if (process.platform === 'win32') {
// A packaged Windows build carries the frozen daemon as an
@@ -1232,6 +1248,8 @@ function registerBridge() {
return { installed: Boolean(bin) };
});
+ ipcMain.handle('node:bundled', () => hasBundledNode());
+
// The systemd unit's own view of the node, for the status panel at the top
// of the Node page. Deliberately not `probeNode()`: that asks the daemon's
// own HTTP API, which cannot answer while the daemon is stopped or crash-
@@ -1245,6 +1263,18 @@ function registerBridge() {
ipcMain.handle('node:service-status', () => nodeServiceStatus());
+ // service-mode.ps1 is an extraResource present in a packaged Full build,
+ // absent from a packaged Light one (nothing to run as a service) and from
+ // an unpackaged dev run. app.isPackaged alone used to gate this, which is
+ // wrong for Light: it would enable the "background service" option and
+ // only fail when actually clicked (winElevateServiceMode's own existsSync
+ // check below, with an actionable error) -- correct but a dead click the
+ // Node page should not offer in the first place.
+ function winCanElevateServiceMode() {
+ return app.isPackaged
+ && fs.existsSync(path.join(process.resourcesPath, 'service-mode.ps1'));
+ }
+
async function nodeServiceStatus() {
if (process.platform === 'win32') {
const svc = await winServiceTaskStatus();
@@ -1259,11 +1289,12 @@ function registerBridge() {
activeState: running ? 'active' : 'inactive',
subState: svc.state,
// Whether switching startup mode can actually elevate right now —
- // service-mode.ps1 is an extraResource, only present in a packaged
- // build. Already installed here, so removing it always works
- // regardless; this only gates the Node page offering to switch
- // *into* service mode.
- canElevate: app.isPackaged,
+ // service-mode.ps1 is an extraResource, present in a packaged Full
+ // build but not a Light one (no node to run as a service at all).
+ // Already installed here, so removing it always works regardless;
+ // this only gates the Node page offering to switch *into* service
+ // mode.
+ canElevate: winCanElevateServiceMode(),
};
}
// Per-user Startup mode. `installed` used to be winAutostartInstalled(),
@@ -1275,12 +1306,17 @@ function registerBridge() {
const [p, bin] = await Promise.all([probeNode(), findNodeBinary()]);
return {
supported: true,
- mode: 'startup',
+ // Only claim "startup mode" once a node was actually found (bundled
+ // or on PATH) -- a Light install with none at all would otherwise
+ // show a working-looking autostart dropdown for a node that does
+ // not exist. `showStartupRow` in node-page.js is gated on this being
+ // a string, so `null` here hides that whole row.
+ mode: bin ? 'startup' : null,
installed: Boolean(bin),
autostart: winAutostartInstalled(),
activeState: p ? 'active' : 'inactive',
subState: p ? 'running' : '',
- canElevate: app.isPackaged,
+ canElevate: winCanElevateServiceMode(),
};
}
if (process.platform !== 'linux') return { supported: false };
diff --git a/packages/meshbay-client/src/preload.js b/packages/meshbay-client/src/preload.js
index 13d4f37..c9c9fe0 100644
--- a/packages/meshbay-client/src/preload.js
+++ b/packages/meshbay-client/src/preload.js
@@ -105,6 +105,10 @@ contextBridge.exposeInMainWorld('meshbay', {
node: {
detect: () => ipcRenderer.invoke('node:detect'),
installed: () => ipcRenderer.invoke('node:installed'),
+ // Whether THIS build ships its own node (Full) or not (Light) --
+ // distinct from `installed`, which also counts one merely found on
+ // PATH. Windows only; other platforms always resolve true.
+ bundled: () => ipcRenderer.invoke('node:bundled'),
start: (opts) => ipcRenderer.invoke('node:start', opts),
call: (method, path, body) => ipcRenderer.invoke('node:call', method, path, body),
pairingCode: () => ipcRenderer.invoke('node:pairing-code'),