runbay 0.1.1 → 0.1.3

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/out/main/index.js CHANGED
@@ -632,7 +632,6 @@ async function scanAll(onBatch, onStatus) {
632
632
  const REGISTRY = process.env.RUNBAY_REGISTRY ?? "https://registry.npmjs.org";
633
633
  const PKG = "runbay";
634
634
  const SIM_CMD = process.env.RUNBAY_SELFUPDATE_CMD;
635
- const SIMULATED = !!SIM_CMD;
636
635
  let available = null;
637
636
  function selfUpdateStatus() {
638
637
  return available;
@@ -658,46 +657,76 @@ async function checkSelfUpdate() {
658
657
  }
659
658
  return available;
660
659
  }
661
- async function installedGlobally() {
660
+ async function globalRoot() {
662
661
  try {
663
- const root = (await run("npm", ["root", "-g"], { shim: true, timeout: 3e4 })).trim();
664
- return electron.app.getAppPath().toLowerCase().startsWith(root.toLowerCase());
662
+ return (await run("npm", ["root", "-g"], { shim: true, timeout: 3e4 })).trim();
665
663
  } catch {
666
- return false;
664
+ return null;
667
665
  }
668
666
  }
667
+ async function installedGlobally() {
668
+ const root = await globalRoot();
669
+ return !!root && electron.app.getAppPath().toLowerCase().startsWith(root.toLowerCase());
670
+ }
671
+ function runCommand(command) {
672
+ return new Promise((resolve2, reject) => {
673
+ const child = node_child_process.spawn(command, { shell: true, windowsHide: true });
674
+ const timer = setTimeout(() => {
675
+ child.kill();
676
+ reject(new Error("npm timed out"));
677
+ }, 5 * 6e4);
678
+ child.on("error", reject);
679
+ child.on("close", (code) => {
680
+ clearTimeout(timer);
681
+ if (code === 0) resolve2();
682
+ else reject(new Error(`npm exited with code ${code}`));
683
+ });
684
+ });
685
+ }
669
686
  async function applySelfUpdate() {
670
687
  if (!available) return { state: "error", message: "No update pending" };
671
688
  const target = available.latest;
672
689
  const registryFlag = REGISTRY === "https://registry.npmjs.org" ? "" : ` --registry "${REGISTRY}"`;
673
- const command = SIM_CMD ?? (await installedGlobally() ? `npm install -g ${PKG}@latest${registryFlag}` : null);
674
- if (!command) {
690
+ if (SIM_CMD) {
691
+ try {
692
+ await runCommand(SIM_CMD);
693
+ } catch (err) {
694
+ return { state: "error", message: err instanceof Error ? err.message : String(err) };
695
+ }
696
+ available = null;
697
+ return { state: "done", message: `Updated to ${target} — restart to apply` };
698
+ }
699
+ if (!await installedGlobally()) {
675
700
  return { state: "manual", message: `npm install -g ${PKG}@latest` };
676
701
  }
702
+ const root = await globalRoot();
703
+ const shim = node_path.join(node_path.dirname(root), `${PKG}.cmd`);
704
+ const script = node_path.join(electron.app.getPath("userData"), "update.cmd");
705
+ const log = node_path.join(electron.app.getPath("userData"), "update.log");
706
+ const install = `call npm install -g ${PKG}@latest${registryFlag}`;
707
+ const body = [
708
+ "@echo off",
709
+ "ping -n 5 127.0.0.1 >nul",
710
+ // le temps que l'app se ferme et relâche ses fichiers
711
+ `${install} > "${log}" 2>&1`,
712
+ "if errorlevel 1 (",
713
+ " ping -n 31 127.0.0.1 >nul",
714
+ // le registre met parfois une minute à propager une publication prouvée
715
+ `${install} --prefer-online >> "${log}" 2>&1`,
716
+ ")",
717
+ `start "" "${shim}"`,
718
+ 'del "%~f0"',
719
+ ""
720
+ ].join("\r\n");
677
721
  try {
678
- await new Promise((resolve2, reject) => {
679
- const child = node_child_process.spawn(command, { shell: true, windowsHide: true });
680
- const timer = setTimeout(() => {
681
- child.kill();
682
- reject(new Error("npm timed out"));
683
- }, 5 * 6e4);
684
- child.on("error", reject);
685
- child.on("close", (code) => {
686
- clearTimeout(timer);
687
- if (code === 0) resolve2();
688
- else reject(new Error(`npm exited with code ${code}`));
689
- });
690
- });
722
+ node_fs.writeFileSync(script, body);
723
+ node_child_process.spawn(script, { detached: true, stdio: "ignore", windowsHide: true }).unref();
691
724
  } catch (err) {
692
725
  return { state: "error", message: err instanceof Error ? err.message : String(err) };
693
726
  }
694
727
  available = null;
695
- if (SIMULATED) return { state: "done", message: `Updated to ${target} — restart to apply` };
696
- setTimeout(() => {
697
- electron.app.relaunch();
698
- electron.app.quit();
699
- }, 1500);
700
- return { state: "done", message: `Updated to ${target} — restarting…` };
728
+ setTimeout(() => electron.app.quit(), 1500);
729
+ return { state: "done", message: `Updating to ${target} — restarting…` };
701
730
  }
702
731
  let scanning = false;
703
732
  let mainHooks = null;
@@ -827,6 +856,7 @@ function registerIpc(hooks) {
827
856
  }
828
857
  return settings.theme === "light" ? "light" : "dark";
829
858
  });
859
+ electron.ipcMain.handle("app:version", () => electron.app.getVersion());
830
860
  electron.ipcMain.handle("update:self:status", () => selfUpdateStatus());
831
861
  electron.ipcMain.handle("update:self:check", () => checkSelfUpdate());
832
862
  electron.ipcMain.handle("update:self:apply", () => applySelfUpdate());
@@ -18,6 +18,7 @@ const api = {
18
18
  welcomeAtStartup: (value) => electron.ipcRenderer.invoke("settings:welcome", value),
19
19
  foregroundOnStart: (value) => electron.ipcRenderer.invoke("settings:foreground", value),
20
20
  theme: (value) => electron.ipcRenderer.invoke("settings:theme", value),
21
+ appVersion: () => electron.ipcRenderer.invoke("app:version"),
21
22
  selfUpdateStatus: () => electron.ipcRenderer.invoke("update:self:status"),
22
23
  checkSelfUpdate: () => electron.ipcRenderer.invoke("update:self:check"),
23
24
  applySelfUpdate: () => electron.ipcRenderer.invoke("update:self:apply"),
@@ -364,6 +364,13 @@ input {
364
364
  justify-content: center;
365
365
  }
366
366
 
367
+ /* Version installée de RunBay : visible sans avoir à chercher dans les menus. */
368
+ .sidebar-footer .version {
369
+ text-align: center;
370
+ font-size: 11px;
371
+ color: var(--text-dim);
372
+ }
373
+
367
374
  /* Signal de mise à jour de RunBay sur le bouton Options : même jaune et même
368
375
  halo pulsé que les mises à jour d'applications — un jaune veut toujours
369
376
  dire « une information attend », jamais un simple décor. */
@@ -1205,6 +1212,14 @@ button.rail-badge.neutral:hover {
1205
1212
  color: var(--warning);
1206
1213
  }
1207
1214
 
1215
+ /* Entrée d'information pure (ex. version de RunBay) : grise, non cliquable. */
1216
+ .ctx-menu button:disabled,
1217
+ .ctx-menu button:disabled:hover {
1218
+ color: var(--text-dim);
1219
+ background: transparent;
1220
+ cursor: default;
1221
+ }
1222
+
1208
1223
  /* Interrupteur on/off d'un réglage, aligné à droite de l'entrée. */
1209
1224
  .ctx-switch {
1210
1225
  margin-left: auto;
@@ -14805,11 +14805,12 @@ function ContextMenu({
14805
14805
  section.items.map((item) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
14806
14806
  "button",
14807
14807
  {
14808
- className: item.danger ? "danger" : item.highlight ? "highlight" : "",
14808
+ className: item.disabled ? "disabled" : item.danger ? "danger" : item.highlight ? "highlight" : "",
14809
14809
  "data-tip": item.tip,
14810
+ disabled: item.disabled,
14810
14811
  onClick: () => {
14811
14812
  if (item.toggle === void 0) onClose();
14812
- item.onSelect();
14813
+ item.onSelect?.();
14813
14814
  },
14814
14815
  children: [
14815
14816
  item.icon && /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { name: item.icon, size: 14 }),
@@ -15037,6 +15038,10 @@ function Sidebar({
15037
15038
  onQuit
15038
15039
  }) {
15039
15040
  const [menu, setMenu] = reactExports.useState(null);
15041
+ const [version, setVersion] = reactExports.useState("");
15042
+ reactExports.useEffect(() => {
15043
+ void window.api.appVersion().then(setVersion);
15044
+ }, []);
15040
15045
  reactExports.useEffect(() => {
15041
15046
  if (!menu) return;
15042
15047
  const onDown = (e) => {
@@ -15061,6 +15066,13 @@ function Sidebar({
15061
15066
  setMenu({ x: rect.left, y: rect.top });
15062
15067
  };
15063
15068
  const runBayItems = [];
15069
+ if (version) {
15070
+ runBayItems.push({
15071
+ label: `Version ${version}`,
15072
+ disabled: true,
15073
+ tip: "This is the version currently running"
15074
+ });
15075
+ }
15064
15076
  if (selfUpdate) {
15065
15077
  runBayItems.push({
15066
15078
  label: selfUpdating ? "Updating RunBay…" : `Update RunBay to v${selfUpdate.latest}`,
@@ -15216,30 +15228,36 @@ RunBay reinstalls itself through npm`,
15216
15228
  );
15217
15229
  })
15218
15230
  ] }),
15219
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sidebar-footer", children: collapsed ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
15220
- "button",
15221
- {
15222
- className: "rail-badge neutral options-btn",
15223
- onClick: openOptions,
15224
- "data-tip": selfUpdate ? "Options — RunBay update available" : "Options",
15225
- children: [
15226
- /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { name: "settings", size: 16 }),
15227
- selfUpdate && !selfUpdating && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "self-dot" })
15228
- ]
15229
- }
15230
- ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
15231
- "button",
15232
- {
15233
- className: "btn secondary options-btn",
15234
- onClick: openOptions,
15235
- "data-tip": selfUpdate ? "Options — RunBay update available" : "Configure RunBay",
15236
- children: [
15237
- /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { name: "settings" }),
15238
- selfUpdate && !selfUpdating && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "self-dot" }),
15239
- " Options"
15240
- ]
15241
- }
15242
- ) }),
15231
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sidebar-footer", children: [
15232
+ collapsed ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
15233
+ "button",
15234
+ {
15235
+ className: "rail-badge neutral options-btn",
15236
+ onClick: openOptions,
15237
+ "data-tip": selfUpdate ? "Options — RunBay update available" : "Options",
15238
+ children: [
15239
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { name: "settings", size: 16 }),
15240
+ selfUpdate && !selfUpdating && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "self-dot" })
15241
+ ]
15242
+ }
15243
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
15244
+ "button",
15245
+ {
15246
+ className: "btn secondary options-btn",
15247
+ onClick: openOptions,
15248
+ "data-tip": selfUpdate ? "Options — RunBay update available" : "Configure RunBay",
15249
+ children: [
15250
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { name: "settings" }),
15251
+ selfUpdate && !selfUpdating && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "self-dot" }),
15252
+ " Options"
15253
+ ]
15254
+ }
15255
+ ),
15256
+ version && !collapsed && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "version", children: [
15257
+ "RunBay v",
15258
+ version
15259
+ ] })
15260
+ ] }),
15243
15261
  menu && /* @__PURE__ */ jsxRuntimeExports.jsx(
15244
15262
  ContextMenu,
15245
15263
  {
@@ -8,8 +8,8 @@
8
8
  http-equiv="Content-Security-Policy"
9
9
  content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
10
10
  />
11
- <script type="module" crossorigin src="./assets/index-aUCFayg4.js"></script>
12
- <link rel="stylesheet" crossorigin href="./assets/index-CsBZ-XBz.css">
11
+ <script type="module" crossorigin src="./assets/index-ew8Rong5.js"></script>
12
+ <link rel="stylesheet" crossorigin href="./assets/index-CVOPEsTH.css">
13
13
  </head>
14
14
  <body>
15
15
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runbay",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Not an installer. A launcher. Detect, launch, update and uninstall apps installed from the command line (npm, pnpm, yarn, bun, scoop) — in one modern window.",
5
5
  "main": "./out/main/index.js",
6
6
  "bin": {