···7676 closeOrHideWindow,
7777 getSystemThemeBackgroundColor,
7878 getPrefs,
7979+ TRANSIENT_ROLES,
7980} from './windows.js';
80818182···20522053 id: win.id,
20532054 role: winInfo?.params?.role as string | undefined,
20542055 });
20562056+20572057+ // Auto-hide sibling transient windows when another window gains
20582058+ // focus. IZUI policy: palette / quick-view / overlay windows are
20592059+ // transient — they should dismiss as soon as focus moves to any
20602060+ // other window. The per-window `blur` handler attached to modals
20612061+ // covers Cmd+Tab and clicks into other apps, but macOS NSPanels
20622062+ // (type:'panel' + alwaysOnTop) don't fire reliable `blur` when
20632063+ // focus shifts to another panel-type window in the same app — so
20642064+ // opening slide B from edge X never closes slide A on edge Y. The
20652065+ // cmd panel restored this on the renderer side via a
20662066+ // `window:focused` subscription (commit 6361cf19); generalize it
20672067+ // here for all transient roles in the main process so we don't
20682068+ // need a per-feature renderer hook for every panel-style tile.
20692069+ //
20702070+ // Skip when the focusing window is itself transient/utility:
20712071+ // - utility (chain popup) is a child of a transient and must
20722072+ // not dismiss its parent
20732073+ // - palette/quick-view/overlay focusing each other would cause
20742074+ // the just-opened transient to immediately close itself in
20752075+ // the wrong order; let the new transient stand and dismiss
20762076+ // the previous one(s).
20772077+ const focusedRole = winInfo?.params?.role as string | undefined;
20782078+ if (focusedRole !== 'utility') {
20792079+ for (const other of BrowserWindow.getAllWindows()) {
20802080+ if (other.id === win.id) continue;
20812081+ if (other.isDestroyed()) continue;
20822082+ if (!other.isVisible()) continue;
20832083+ const otherInfo = getWindowInfo(other.id);
20842084+ const otherRole = otherInfo?.params?.role as string | undefined;
20852085+ if (!otherRole || !TRANSIENT_ROLES.has(otherRole)) continue;
20862086+ // Overlays manage their own restoration flow via the IZUI
20872087+ // coordinator — don't drive them through closeOrHideWindow.
20882088+ if (otherRole === 'overlay') continue;
20892089+ DEBUG && console.log('[izui] window-focused autoclose transient sibling:', other.id, 'role:', otherRole, 'focused:', win.id, 'role:', focusedRole);
20902090+ try {
20912091+ closeOrHideWindow(other.id);
20922092+ } catch (err) {
20932093+ DEBUG && console.log('[izui] autoclose transient failed:', err);
20942094+ }
20952095+ }
20962096+ }
20552097 });
20562098 // Also track immediately if this is a content/visible window (non-modal only)
20572099 trackContentWindowFocus(win);
+8-1
backend/electron/windows.ts
···338338 * These are modal/overlay windows (cmd palette, quick-view, windows switcher)
339339 * that open and close frequently. Counting them causes dock show/hide cycling
340340 * which triggers activation policy changes and spurious blur events.
341341+ *
342342+ * Also used by the focus-driven auto-hide path: when a non-transient,
343343+ * non-utility window gains focus, any other window with one of these roles
344344+ * is dismissed via closeOrHideWindow. Restores the "open another window
345345+ * → previous transient closes" behavior that panel-type windows lose
346346+ * because macOS NSPanels don't fire reliable per-window blur events when
347347+ * focus shifts within the app.
341348 */
342342-const TRANSIENT_ROLES = new Set(['palette', 'quick-view', 'overlay']);
349349+export const TRANSIENT_ROLES = new Set(['palette', 'quick-view', 'overlay']);
343350344351/**
345352 * Get count of visible persistent windows (excluding background, modals, overlays, HUD).