@amd-gaia/agent-ui 0.25.4-rc.12 → 0.25.4-rc.13

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.
@@ -1 +1 @@
1
- import{K as e}from"./useT-DBWm7Kq3.js";var t={name:`message-square`,size:24,node:[[`path`,{d:`M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z`,key:`18887p`}]]};t.node;var n=e(t),r={name:`zap`,size:24,node:[[`path`,{d:`M15.914 4a1.5 1.5 0 00-2.474-1.561l-9 9A1.5 1.5 0 005.5 14h4.002a.5.5 0 01.471.666L8.086 20a1.5 1.5 0 002.475 1.56l9-9A1.5 1.5 0 0018.5 10h-3.997a.5.5 0 01-.472-.667z`,key:`1v7up4`}]]};r.node;var i=e(r);export{n,i as t};
1
+ import{K as e}from"./useT-TIhGZVZ6.js";var t={name:`message-square`,size:24,node:[[`path`,{d:`M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z`,key:`18887p`}]]};t.node;var n=e(t),r={name:`zap`,size:24,node:[[`path`,{d:`M15.914 4a1.5 1.5 0 00-2.474-1.561l-9 9A1.5 1.5 0 005.5 14h4.002a.5.5 0 01.471.666L8.086 20a1.5 1.5 0 002.475 1.56l9-9A1.5 1.5 0 0018.5 10h-3.997a.5.5 0 01-.472-.667z`,key:`1v7up4`}]]};r.node;var i=e(r);export{n,i as t};
package/dist/index.html CHANGED
@@ -12,11 +12,11 @@
12
12
  <meta name="apple-mobile-web-app-capable" content="yes" />
13
13
  <meta name="apple-mobile-web-app-title" content="GAIA" />
14
14
  <title>GAIA Agent UI</title>
15
- <script type="module" crossorigin src="./assets/index-Dri99bxV.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-DsqWzb7b.js"></script>
16
16
  <link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-XKyaHtl9.js">
17
- <link rel="modulepreload" crossorigin href="./assets/useT-DBWm7Kq3.js">
18
- <link rel="modulepreload" crossorigin href="./assets/api-CVebTC8m.js">
19
- <link rel="stylesheet" crossorigin href="./assets/index-BnhHRhFR.css">
17
+ <link rel="modulepreload" crossorigin href="./assets/useT-TIhGZVZ6.js">
18
+ <link rel="modulepreload" crossorigin href="./assets/api-CMkT9jku.js">
19
+ <link rel="stylesheet" crossorigin href="./assets/index-B7tI0eKR.css">
20
20
  </head>
21
21
  <body>
22
22
  <div id="root"></div>
package/dist/sw.js CHANGED
@@ -9,7 +9,7 @@
9
9
  // never sees a stale UI or stale data.
10
10
 
11
11
  const CACHE_PREFIX = 'gaia-assets-';
12
- const CACHE_NAME = CACHE_PREFIX + '0.25.4rc12-ec1c81709fd02367';
12
+ const CACHE_NAME = CACHE_PREFIX + '0.25.4rc13-d290fdd1dd302aec';
13
13
  const ASSETS_PATH = new URL('assets/', self.registration.scope).pathname;
14
14
 
15
15
  self.addEventListener('install', () => {
package/main.cjs CHANGED
@@ -85,6 +85,7 @@ const _inSafeMode = _crashCount >= SAFE_MODE_THRESHOLD;
85
85
 
86
86
  // Services (loaded after app.whenReady)
87
87
  const TrayManager = require("./services/tray-manager.cjs");
88
+ const { probeTrayHost } = require("./services/tray-host.cjs");
88
89
  const { appIconPath, appUserModelId } = require("./services/app-icon.cjs");
89
90
  const {
90
91
  attachWindowGuards,
@@ -257,6 +258,8 @@ let engineInstall = null;
257
258
 
258
259
  /** @type {TrayManager | null} */
259
260
  let trayManager = null;
261
+ /** Settles once the tray knows whether this desktop shows tray icons. */
262
+ let trayHostReady = null;
260
263
 
261
264
  /** @type {AgentProcessManager | null} */
262
265
  let agentProcessManager = null;
@@ -666,15 +669,20 @@ function createWindow() {
666
669
  // Show window when ready (unless --minimized or startMinimized config)
667
670
  mainWindow.once("ready-to-show", () => {
668
671
  console.log("[main] ready-to-show fired");
669
- const shouldStartMinimized =
670
- startMinimized || (trayManager && trayManager.startMinimized);
671
-
672
- if (!shouldStartMinimized) {
673
- console.log("[main] mainWindow.show() called");
674
- mainWindow.show();
675
- } else {
676
- console.log("[main] Starting minimized to tray");
677
- }
672
+ Promise.resolve(trayHostReady).then(() => {
673
+ if (!mainWindow || mainWindow.isDestroyed()) return;
674
+ // Never start hidden where no tray icon would bring the window back.
675
+ const canHide = Boolean(trayManager && trayManager.trayHost.available);
676
+ const shouldStartMinimized =
677
+ canHide && (startMinimized || trayManager.startMinimized);
678
+
679
+ if (!shouldStartMinimized) {
680
+ console.log("[main] mainWindow.show() called");
681
+ mainWindow.show();
682
+ } else {
683
+ console.log("[main] Starting minimized to tray");
684
+ }
685
+ });
678
686
  });
679
687
 
680
688
  return mainWindow;
@@ -782,6 +790,14 @@ function initializeServices() {
782
790
  reopenWindow: () => reopenWindow(),
783
791
  });
784
792
  trayManager.create();
793
+ trayHostReady = probeTrayHost()
794
+ .then((host) => {
795
+ if (trayManager) trayManager.setHost(host);
796
+ })
797
+ .catch((err) => {
798
+ // The host stays unknown, so closing quits instead of hiding.
799
+ console.error(`[main] Could not record the tray host: ${err.message}`);
800
+ });
785
801
  try {
786
802
  trayManager.syncLoginItem();
787
803
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amd-gaia/agent-ui",
3
- "version": "0.25.4-rc.12",
3
+ "version": "0.25.4-rc.13",
4
4
  "type": "module",
5
5
  "productName": "GAIA Agent UI",
6
6
  "description": "Chat with the GAIA agent - local and private by default on AMD Ryzen AI, cloud models when you choose them",
@@ -0,0 +1,84 @@
1
+ // Copyright(C) 2026 Advanced Micro Devices, Inc. All rights reserved.
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ /**
5
+ * Whether this desktop can show GAIA's tray icon at all.
6
+ *
7
+ * Windows and macOS always can. On Linux, Electron's tray is a
8
+ * StatusNotifierItem, which shows only when a host owns the
9
+ * `org.kde.StatusNotifierWatcher` name on the session bus. Stock GNOME has
10
+ * none (its AppIndicator extension adds one), so hiding the window there
11
+ * leaves GAIA running with no way back to it.
12
+ */
13
+
14
+ const { execFile } = require("child_process");
15
+
16
+ const WATCHER = "org.kde.StatusNotifierWatcher";
17
+ const PROBE_TIMEOUT_MS = 2000;
18
+
19
+ /** The two bus clients a Linux desktop ships, and how each reports NameHasOwner. */
20
+ const PROBES = [
21
+ {
22
+ cmd: "gdbus",
23
+ args: [
24
+ "call", "--session",
25
+ "--dest", "org.freedesktop.DBus",
26
+ "--object-path", "/org/freedesktop/DBus",
27
+ "--method", "org.freedesktop.DBus.NameHasOwner", WATCHER,
28
+ ],
29
+ parse: (out) => /\(\s*true\s*,?\s*\)/.test(out) ? true : /\(\s*false\s*,?\s*\)/.test(out) ? false : null,
30
+ },
31
+ {
32
+ cmd: "dbus-send",
33
+ args: [
34
+ "--session", "--print-reply", "--dest=org.freedesktop.DBus",
35
+ "/org/freedesktop/DBus", "org.freedesktop.DBus.NameHasOwner", `string:${WATCHER}`,
36
+ ],
37
+ parse: (out) => /boolean\s+true/.test(out) ? true : /boolean\s+false/.test(out) ? false : null,
38
+ },
39
+ ];
40
+
41
+ function runCommand(cmd, args) {
42
+ return new Promise((resolve, reject) => {
43
+ execFile(cmd, args, { timeout: PROBE_TIMEOUT_MS, encoding: "utf8" }, (err, stdout, stderr) => {
44
+ if (err) {
45
+ err.stderr = stderr;
46
+ reject(err);
47
+ return;
48
+ }
49
+ resolve(stdout);
50
+ });
51
+ });
52
+ }
53
+
54
+ /**
55
+ * @typedef {{available: boolean, reason: null | "no-host" | "check-failed", detail?: string}} TrayHost
56
+ * `no-host`: the bus answered and nothing hosts tray icons.
57
+ * `check-failed`: the bus couldn't be asked; treated as no tray, so closing never strands the app.
58
+ */
59
+
60
+ /**
61
+ * @param {{platform?: string, run?: (cmd: string, args: string[]) => Promise<string>}} [deps]
62
+ * @returns {Promise<TrayHost>}
63
+ */
64
+ async function probeTrayHost({ platform = process.platform, run = runCommand } = {}) {
65
+ if (platform !== "linux") return { available: true, reason: null };
66
+ const failures = [];
67
+ for (const probe of PROBES) {
68
+ let out;
69
+ try {
70
+ out = await run(probe.cmd, probe.args);
71
+ } catch (err) {
72
+ const why = err.code === "ENOENT" ? "not installed" : (err.stderr || err.message || String(err)).trim();
73
+ failures.push(`${probe.cmd}: ${why}`);
74
+ continue;
75
+ }
76
+ const owned = probe.parse(out);
77
+ if (owned === true) return { available: true, reason: null };
78
+ if (owned === false) return { available: false, reason: "no-host", detail: `nothing owns ${WATCHER}` };
79
+ failures.push(`${probe.cmd}: unexpected reply ${JSON.stringify(out.trim())}`);
80
+ }
81
+ return { available: false, reason: "check-failed", detail: failures.join("; ") };
82
+ }
83
+
84
+ module.exports = { probeTrayHost, WATCHER, PROBE_TIMEOUT_MS };
@@ -200,6 +200,9 @@ class TrayManager {
200
200
 
201
201
  this._iconSpec = ICON_SPECS[process.platform] || ICON_SPECS.linux;
202
202
 
203
+ /** Whether the desktop shows tray icons; on Linux unknown (null) until setHost(). */
204
+ this._host = process.platform === "linux" ? null : { available: true, reason: null };
205
+
203
206
  /** @type {Map<string, Electron.NativeImage>} One image per tray state, built on first use. */
204
207
  this._icons = new Map();
205
208
 
@@ -330,14 +333,37 @@ class TrayManager {
330
333
  return this._notificationCount || 0;
331
334
  }
332
335
 
333
- /** @returns {boolean} Whether minimize-to-tray is enabled. */
336
+ /** @returns {boolean} Whether closing hides the window: switched on, and a tray to find it in. */
334
337
  get minimizeToTray() {
335
- return this.config.tray.minimizeToTray;
338
+ return this.config.tray.minimizeToTray && this.trayHost.available;
336
339
  }
337
340
 
338
- /** @returns {boolean} Whether app should start minimized. */
341
+ /** @returns {boolean} Whether app should start minimized, which also needs a tray. */
339
342
  get startMinimized() {
340
- return this.config.tray.startMinimized;
343
+ return this.config.tray.startMinimized && this.trayHost.available;
344
+ }
345
+
346
+ /** @returns {{available: boolean, reason: null | "checking" | "no-host" | "check-failed"}} */
347
+ get trayHost() {
348
+ return this._host || { available: false, reason: "checking" };
349
+ }
350
+
351
+ /** Record what tray-host.cjs found; until then a Linux window is never hidden. */
352
+ setHost(host) {
353
+ if (!isPlainObject(host) || typeof host.available !== "boolean") {
354
+ throw new TypeError("TrayManager.setHost expects {available: boolean, reason}");
355
+ }
356
+ this._host = { available: host.available, reason: host.available ? null : host.reason };
357
+ if (!host.available) {
358
+ console.log(
359
+ `[tray] No tray icon host (${host.reason}: ${host.detail || "no detail"}); closing the window quits GAIA`
360
+ );
361
+ }
362
+ }
363
+
364
+ /** The saved config plus the tray host, which Settings needs and nothing saves. */
365
+ _configReply() {
366
+ return { ...this.config, host: this.trayHost };
341
367
  }
342
368
 
343
369
  /** @returns {boolean} Whether app should start on login. */
@@ -722,7 +748,7 @@ class TrayManager {
722
748
  });
723
749
 
724
750
  ipcMain.handle("tray:get-config", () => {
725
- return this.config;
751
+ return this._configReply();
726
752
  });
727
753
 
728
754
  ipcMain.handle("tray:set-config", (_event, cfg) => {
@@ -750,7 +776,7 @@ class TrayManager {
750
776
  }
751
777
 
752
778
  if (tray === null && agents === null) {
753
- return this.config;
779
+ return this._configReply();
754
780
  }
755
781
  const loginChanged =
756
782
  tray !== null && tray.startOnLogin !== this.config.tray.startOnLogin;
@@ -774,7 +800,7 @@ class TrayManager {
774
800
  }
775
801
  this.config = next;
776
802
 
777
- return this.config;
803
+ return this._configReply();
778
804
  });
779
805
  }
780
806