···119119120120See `notes/escape-navigation.md` for full design details.
121121122122+## Dock / App Switcher Visibility (macOS)
123123+124124+The dock icon visibility is dynamic based on window state and user preference:
125125+126126+- **Windows visible**: Dock icon shown (regardless of preference)
127127+- **No windows visible**: Dock icon hidden (unless preference enabled)
128128+129129+The `showInDockAndSwitcher` preference controls whether to *always* show the dock icon, even when no windows are open. When disabled (default), the dock icon only appears while Peek windows are visible.
130130+131131+This is implemented via:
132132+- `getVisibleWindowCount()` - counts non-background visible windows
133133+- `updateDockVisibility()` - shows/hides dock based on window count + pref
134134+- Called from: window-open, window-show, maybeHideApp, prefs change
135135+122136## App Icon Generation
123137124138The macOS app icon is generated from a source PNG using ImageMagick. The process applies rounded corners and adds padding to match macOS icon guidelines.
+2-2
app/config.js
···4040 "default": true
4141 },
4242 "showInDockAndSwitcher": {
4343- "description": "Whether to hide or show app in OS dock and app switcher",
4343+ "description": "Always show in dock/app switcher, even when no windows are open. When disabled, dock icon only appears while windows are visible.",
4444 "type": "boolean",
4545 "default": false
4646 },
···117117 width: 800,
118118 startupFeature: 'peek://app/settings/settings.html',
119119 showTrayIcon: true,
120120- showInTrayAndSwitcher: false
120120+ showInDockAndSwitcher: false
121121 },
122122 items: [
123123 { id: 'cee1225d-40ac-41e5-a34c-e2edba69d599',
+2
app/settings/settings.js
···104104 const save = () => {
105105 store.set(storageKeys.PREFS, prefs);
106106 store.set(storageKeys.ITEMS, features);
107107+ // Notify main process of prefs change for live updates (quit shortcut, dock visibility, etc.)
108108+ api.publish('topic:core:prefs', { id, prefs }, api.scopes.SYSTEM);
107109 };
108110109111 // Preferences
+44-23
index.js
···473473 console.log('onReady');
474474475475 // Hide dock early to prevent flash in app switcher
476476- // Will be shown later if showInDockAndSwitcher pref is true
477477- if (app.dock && !DEBUG) {
478478- console.log('hiding dock early (non-debug mode)');
476476+ // Will be shown/hidden properly once prefs are loaded
477477+ if (app.dock) {
479478 app.dock.hide();
480479 }
481480···568567 // cache all prefs
569568 _prefs = msg.prefs;
570569571571- // show/hide in dock and tab switcher based on preference
572572- if (app.dock) {
573573- if (msg.prefs.showInDockAndSwitcher === true) {
574574- console.log('showing dock (pref enabled)');
575575- app.dock.show();
576576- } else {
577577- console.log('hiding dock (pref disabled)');
578578- app.dock.hide();
579579- }
580580- }
570570+ // Update dock visibility based on pref and visible windows
571571+ updateDockVisibility();
581572582573 // initialize system tray
583574 if (msg.prefs.showTrayIcon == true) {
···951942 });
952943 }
953944945945+ // Show dock when window opens
946946+ updateDockVisibility();
947947+954948 return { success: true, id: win.id };
955949 } catch (error) {
956950 console.error('Failed to open window:', error);
···10311025 }
1032102610331027 win.show();
10281028+ updateDockVisibility();
10341029 return { success: true };
10351030 } catch (error) {
10361031 console.error('Failed to show window:', error);
···19511946 }
19521947};
1953194819541954-// Only hide the app if there are no other visible windows (besides the one being closed/hidden)
19551955-const maybeHideApp = (excludeId) => {
19561956- if (process.platform !== 'darwin') return;
19571957-19581958- // Check if there are any other visible windows
19591959- const visibleWindows = BrowserWindow.getAllWindows().filter(win => {
19601960- if (win.id === excludeId) return false;
19491949+/**
19501950+ * Get count of visible user windows (excluding background window)
19511951+ */
19521952+const getVisibleWindowCount = (excludeId = null) => {
19531953+ return BrowserWindow.getAllWindows().filter(win => {
19541954+ if (excludeId && win.id === excludeId) return false;
19611955 if (win.isDestroyed()) return false;
19621956 if (!win.isVisible()) return false;
19631957···19661960 if (entry && entry.params.address === webCoreAddress) return false;
1967196119681962 return true;
19691969- });
19631963+ }).length;
19641964+};
1970196519711971- console.log('maybeHideApp: visible windows (excluding', excludeId + '):', visibleWindows.length);
19661966+/**
19671967+ * Update dock visibility based on visible windows and pref
19681968+ * Show dock if: visible windows exist OR pref is enabled
19691969+ * Hide dock if: no visible windows AND pref is disabled
19701970+ */
19711971+const updateDockVisibility = (excludeId = null) => {
19721972+ if (process.platform !== 'darwin' || !app.dock) return;
1972197319731973- if (visibleWindows.length === 0) {
19741974+ const visibleCount = getVisibleWindowCount(excludeId);
19751975+ const prefShowDock = _prefs?.showInDockAndSwitcher === true;
19761976+19771977+ if (visibleCount > 0 || prefShowDock) {
19781978+ app.dock.show();
19791979+ } else {
19801980+ app.dock.hide();
19811981+ }
19821982+};
19831983+19841984+// Only hide the app if there are no other visible windows (besides the one being closed/hidden)
19851985+const maybeHideApp = (excludeId) => {
19861986+ if (process.platform !== 'darwin') return;
19871987+19881988+ const visibleCount = getVisibleWindowCount(excludeId);
19891989+ console.log('maybeHideApp: visible windows (excluding', excludeId + '):', visibleCount);
19901990+19911991+ if (visibleCount === 0) {
19741992 console.log('No other visible windows, hiding app');
19751993 app.hide();
19761994 } else {
19771995 console.log('Other windows visible, not hiding app');
19781996 }
19971997+19981998+ // Also update dock visibility
19991999+ updateDockVisibility(excludeId);
19792000};
1980200119812002const closeOrHideWindow = id => {