aboutsummaryrefslogtreecommitdiff
path: root/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai
diff options
context:
space:
mode:
authorRyan Schanzenbacher <ryan@rschanz.org>2026-07-09 22:15:53 -0400
committerRyan Schanzenbacher <ryan@rschanz.org>2026-07-09 22:15:53 -0400
commitd06784958d73159b5e4abafe5114804662114ed7 (patch)
treed8464262c1aa1f10553a5b11a9fda3e505e71f7f /users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai
parent1a71e94eb8ca1585201263cf654611ef6849d359 (diff)
move ubersicht modules in tree
Diffstat (limited to 'users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai')
-rwxr-xr-xusers/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/opened-apps.jsx55
-rwxr-xr-xusers/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/process.jsx102
-rwxr-xr-xusers/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/space-options.jsx84
-rwxr-xr-xusers/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/space.jsx194
-rwxr-xr-xusers/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/spaces.jsx109
-rwxr-xr-xusers/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/stickies.jsx56
-rwxr-xr-xusers/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/window.jsx119
7 files changed, 719 insertions, 0 deletions
diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/opened-apps.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/opened-apps.jsx
new file mode 100755
index 0000000..744867e
--- /dev/null
+++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/opened-apps.jsx
@@ -0,0 +1,55 @@
1import * as Uebersicht from "uebersicht";
2import * as AppIcons from "../../app-icons.js";
3import { SuspenseIcon } from "../icons/icon.jsx";
4import * as Utils from "../../utils.js";
5
6const { React } = Uebersicht;
7
8/**
9 * OpenedApps component to display a list of opened applications.
10 * @param {Object} props - Component properties.
11 * @param {Array} props.apps - The list of opened applications.
12 * @returns {JSX.Element|null} The rendered component or null if no applications are present.
13 */
14export default function OpenedApps({ apps }) {
15 // Return null if no applications are present
16 if (!apps.length) return null;
17
18 // Sort and map the applications to JSX elements
19 return Utils.sortWindows(apps).map((app, i) => {
20 const {
21 "is-minimized": isMinimized,
22 minimized: __legacyIsMinimized,
23 "has-focus": hasFocus,
24 focused: __legacyHasFocus,
25 "has-parent-zoom": hasParentZoom,
26 "zoom-parent": __legacyHasParentZoom,
27 "has-fullscreen-zoom": hasFullscreenZoom,
28 "zoom-fullscreen": __legacyHasFullscreenZoom,
29 "is-topmost": isTopmost,
30 } = app;
31 const appName = Utils.normalizeAppName(app.app);
32
33 // Skip minimized applications
34 if (isMinimized ?? __legacyIsMinimized) return null;
35
36 // Get the application icon or default icon
37 const Icon = AppIcons.apps[appName] || AppIcons.apps.Default;
38
39 // Determine the CSS classes for the icon
40 const classes = Utils.classNames("space__icon", {
41 "space__icon--focused": hasFocus ?? __legacyHasFocus,
42 "space__icon--fullscreen":
43 (hasParentZoom ?? __legacyHasParentZoom) ||
44 (hasFullscreenZoom ?? __legacyHasFullscreenZoom),
45 "space__icon--topmost": isTopmost,
46 });
47
48 // Render the application icon
49 return (
50 <SuspenseIcon key={i}>
51 <Icon className={classes} />
52 </SuspenseIcon>
53 );
54 });
55}
diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/process.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/process.jsx
new file mode 100755
index 0000000..96a0b7c
--- /dev/null
+++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/process.jsx
@@ -0,0 +1,102 @@
1import * as Uebersicht from "uebersicht";
2import Window from "./window.jsx";
3import * as Utils from "../../utils";
4import { useYabaiContext } from "../yabai-context.jsx";
5import { useSimpleBarContext } from "../simple-bar-context.jsx";
6
7export { processStyles as styles } from "../../styles/components/process";
8
9const { React } = Uebersicht;
10
11/**
12 * Process component that displays the current windows and spaces information.
13 * @returns {JSX.Element|null} The rendered component or null if not visible.
14 */
15const Component = React.memo(() => {
16 // Get context values from yabai and simple-bar
17 const { spaces, windows, skhdMode } = useYabaiContext();
18 const { displayIndex, settings } = useSimpleBarContext();
19 const { process, spacesDisplay, widgets } = settings;
20 const { processWidget } = widgets;
21 const { exclusionsAsRegex } = spacesDisplay;
22 const { displayForFocusedSpace, centered, showCurrentSpaceMode, displaySkhdMode, showOnDisplay } =
23 process;
24
25 // Determine if the process widget should be visible
26 const visible =
27 processWidget &&
28 Utils.isVisibleOnDisplay(displayIndex, showOnDisplay) &&
29 windows;
30
31 if (!visible) return null;
32
33 // Parse exclusions based on settings
34 const exclusions = exclusionsAsRegex
35 ? spacesDisplay.exclusions
36 : spacesDisplay.exclusions.split(", ");
37
38 const titleExclusions = exclusionsAsRegex
39 ? spacesDisplay.titleExclusions
40 : spacesDisplay.titleExclusions.split(", ");
41
42 // Find the current space based on visibility and display index
43 const currentSpace = spaces.find((space) => {
44 const {
45 "is-visible": isVisible,
46 "has-focus" : hasFocus,
47 visible: __legacyIsVisible,
48 display,
49 } = space;
50 return (isVisible ?? __legacyIsVisible) && (displayForFocusedSpace ? hasFocus : display === displayIndex);
51 });
52
53 // Get sticky and non-sticky windows using a utility function
54 const { stickyWindows, nonStickyWindows } = Utils.stickyWindowWorkaround({
55 windows,
56 uniqueApps: false,
57 currentDisplay: displayIndex,
58 currentSpace: currentSpace?.index,
59 exclusions,
60 titleExclusions,
61 exclusionsAsRegex,
62 });
63
64 // Combine sticky and non-sticky windows
65 const apps = [...stickyWindows, ...nonStickyWindows];
66
67 // Determine CSS classes for the component
68 const classes = Utils.classNames("process", {
69 "process--centered": centered,
70 });
71
72 // Determine the current skhd mode and its color
73 const currentSkhdMode = skhdMode.mode === "default" ? null : skhdMode.mode;
74 const skhdModeColor = "var(--" + skhdMode.color + ")";
75
76 return (
77 <div className={classes}>
78 <div className="process__container">
79 {showCurrentSpaceMode && currentSpace && (
80 <div key={currentSpace.index} className="process__layout">
81 {currentSpace.type}
82 </div>
83 )}
84 {displaySkhdMode && currentSkhdMode && (
85 <div
86 className="process__skhd-mode"
87 style={{ backgroundColor: skhdModeColor }}
88 >
89 {currentSkhdMode}
90 </div>
91 )}
92 {Utils.sortWindows(apps).map((window, i) => (
93 <Window key={i} window={window} />
94 ))}
95 </div>
96 </div>
97 );
98});
99
100Component.displayName = "Process";
101
102export default Component;
diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/space-options.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/space-options.jsx
new file mode 100755
index 0000000..9c54d8b
--- /dev/null
+++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/space-options.jsx
@@ -0,0 +1,84 @@
1import * as Uebersicht from "uebersicht";
2import * as Icons from "../icons/icons.jsx";
3import { SuspenseIcon } from "../icons/icon.jsx";
4import { useSimpleBarContext } from "../simple-bar-context.jsx";
5import * as Utils from "../../utils.js";
6import * as Yabai from "../../yabai.js";
7
8const { React } = Uebersicht;
9
10/**
11 * SpaceOptions component provides options to manipulate spaces (move left, move right, remove).
12 * @param {Object} props - The component props.
13 * @param {number} props.index - The index of the space.
14 * @param {Function} props.setHovered - Function to set the hovered state.
15 * @returns {JSX.Element} The SpaceOptions component.
16 */
17export default function SpaceOptions({ index, setHovered }) {
18 // Get displayIndex from the context
19 const { displayIndex } = useSimpleBarContext();
20
21 /**
22 * Handles the click event to remove a space.
23 * @param {Event} e - The click event.
24 */
25 const remove = async (e) => {
26 e.stopPropagation();
27 Utils.clickEffect(e);
28 setHovered(false);
29 await Yabai.removeSpace(index, displayIndex);
30 };
31
32 /**
33 * Returns a function to handle the click event to swap a space.
34 * @param {string} direction - The direction to swap the space ("left" or "right").
35 * @returns {Function} The click event handler.
36 */
37 const moveTo = (direction) => async (e) => {
38 Utils.clickEffect(e);
39 setHovered(false);
40 await Yabai.swapSpace(index, direction);
41 };
42
43 /**
44 * Prevents the default behavior of the mouse down event.
45 * @param {Event} e - The mouse down event.
46 */
47 const onMouseDown = (e) => e.preventDefault();
48
49 /**
50 * Handles the mouse leave event to unset the hovered state.
51 */
52 const onMouseLeave = () => setHovered(false);
53
54 return (
55 <span className="space-options" onMouseLeave={onMouseLeave}>
56 <div
57 className="space-options__option space-options__option--move-prev"
58 onMouseDown={onMouseDown}
59 onClick={moveTo("left")}
60 >
61 <SuspenseIcon>
62 <Icons.ChevronLeft />
63 </SuspenseIcon>
64 </div>
65 <div
66 className="space-options__option space-options__option--move-next"
67 onMouseDown={onMouseDown}
68 onClick={moveTo("right")}
69 >
70 <SuspenseIcon>
71 <Icons.ChevronRight />
72 </SuspenseIcon>
73 </div>
74 <div
75 className="space-options__option space-options__option--remove"
76 onClick={remove}
77 >
78 <SuspenseIcon>
79 <Icons.Remove />
80 </SuspenseIcon>
81 </div>
82 </span>
83 );
84}
diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/space.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/space.jsx
new file mode 100755
index 0000000..3b5e761
--- /dev/null
+++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/space.jsx
@@ -0,0 +1,194 @@
1import * as Uebersicht from "uebersicht";
2import OpenedApps from "./opened-apps.jsx";
3import SpaceOptions from "./space-options.jsx";
4import { useYabaiContext } from "../yabai-context.jsx";
5import { useSimpleBarContext } from "../simple-bar-context.jsx";
6import * as Utils from "../../utils.js";
7import * as Yabai from "../../yabai.js";
8
9const { React } = Uebersicht;
10
11/**
12 * Component representing a space in yabai window manager.
13 * @param {Object} props - The component props.
14 * @param {Object} props.space - The space object.
15 * @param {number} props.display - The display index.
16 * @param {number} props.currentSpaceIndex - The current space index.
17 * @param {boolean} props.lastOfSpace - Whether this is the last space.
18 * @returns {JSX.Element|null} The rendered component.
19 */
20export default function Space({
21 space,
22 display,
23 currentSpaceIndex,
24 lastOfSpace,
25}) {
26 const { windows } = useYabaiContext();
27 const { SIPDisabled, settings } = useSimpleBarContext();
28 const { spacesDisplay } = settings;
29 const {
30 displayAllSpacesOnAllScreens,
31 exclusionsAsRegex,
32 displayStickyWindowsSeparately,
33 hideDuplicateAppsInSpaces,
34 showOptionsOnHover,
35 } = spacesDisplay;
36
37 const labelRef = React.useRef();
38 const [hovered, setHovered] = React.useState(false);
39 const [noDelay, setNoDelay] = React.useState(false);
40 const [editable, setEditable] = React.useState(false);
41 const {
42 index,
43 label,
44 "has-focus": hasFocus,
45 focused: __legacyHasFocus,
46 "is-visible": isVisible,
47 visible: __legacyIsVisible,
48 "is-native-fullscreen": isNativeFullscreen,
49 "native-fullscreen": __legacyIsNativeFullscreen,
50 type,
51 } = space;
52 const [spaceLabel, setSpaceLabel] = React.useState(
53 label?.length ? label : index,
54 );
55
56 // Return null if the space should not be displayed on the current screen
57 if (!displayAllSpacesOnAllScreens && display.index !== space.display)
58 return null;
59
60 const exclusions = exclusionsAsRegex
61 ? spacesDisplay.exclusions
62 : spacesDisplay.exclusions.split(", ");
63 const titleExclusions = exclusionsAsRegex
64 ? spacesDisplay.titleExclusions
65 : spacesDisplay.titleExclusions.split(", ");
66
67 /**
68 * Handle mouse enter event.
69 * @param {MouseEvent} e - The mouse event.
70 */
71 const onMouseEnter = (e) => {
72 if (!showOptionsOnHover) return;
73 const { altKey, metaKey } = e;
74 if (altKey) return;
75 setHovered(true);
76 if (metaKey) setNoDelay(true);
77 };
78
79 /**
80 * Handle mouse leave event.
81 */
82 const onMouseLeave = () => {
83 setHovered(false);
84 setNoDelay(false);
85 setEditable(false);
86 window.getSelection().removeAllRanges();
87 };
88
89 /**
90 * Handle click event.
91 * @param {MouseEvent} e - The mouse event.
92 */
93 const onClick = (e) => {
94 onMouseLeave(e);
95 if (e.altKey) {
96 setEditable(true);
97 labelRef.current?.select();
98 return;
99 }
100 if (hasFocus || __legacyHasFocus) return;
101 if (SIPDisabled && !spacesDisplay.switchSpacesWithoutYabai) {
102 Yabai.goToSpace(index);
103 Utils.clickEffect(e);
104 return;
105 }
106 Utils.switchSpace(currentSpaceIndex, index);
107 Utils.clickEffect(e);
108 };
109
110 /**
111 * Handle right-click event.
112 */
113 const onRightClick = () => {
114 setHovered(true);
115 setNoDelay(true);
116 };
117
118 /**
119 * Handle change event for the space label input.
120 * @param {Event} e - The change event.
121 */
122 const onChange = (e) => {
123 if (!editable) return;
124 const newLabel = e.target.value;
125 setSpaceLabel(newLabel);
126 Yabai.renameSpace(index, newLabel);
127 };
128
129 const { nonStickyWindows: apps, stickyWindows } =
130 Utils.stickyWindowWorkaround({
131 windows,
132 uniqueApps: hideDuplicateAppsInSpaces,
133 currentDisplay: display,
134 currentSpace: index,
135 exclusions,
136 titleExclusions,
137 exclusionsAsRegex,
138 });
139 const allApps = [...apps, ...stickyWindows];
140
141 // Determine if the space should be hidden
142 const hidden =
143 !(hasFocus ?? __legacyHasFocus) &&
144 !(isVisible ?? __legacyHasFocus) &&
145 !allApps.length &&
146 spacesDisplay.hideEmptySpaces;
147
148 if (hidden) return null;
149
150 const classes = Utils.classNames(`space space--${type}`, {
151 "space--focused": hasFocus ?? __legacyHasFocus,
152 "space--visible": isVisible ?? __legacyIsVisible,
153 "space--fullscreen": isNativeFullscreen ?? __legacyIsNativeFullscreen,
154 "space--hovered": hovered,
155 "space--no-delay": noDelay,
156 "space--empty": allApps.length,
157 "space--editable": editable,
158 });
159
160 const labelSize = (
161 typeof spaceLabel === "number" ? spaceLabel.toString() : spaceLabel
162 ).length;
163
164 return (
165 <React.Fragment>
166 {displayAllSpacesOnAllScreens && lastOfSpace && (
167 <div className="spaces__separator" />
168 )}
169 <div
170 className={classes}
171 onMouseLeave={onMouseLeave}
172 onMouseEnter={onMouseEnter}
173 >
174 <button
175 className="space__inner"
176 onClick={onClick}
177 onContextMenu={onRightClick}
178 >
179 <input
180 ref={labelRef}
181 type="text"
182 className="space__label"
183 onChange={onChange}
184 value={spaceLabel}
185 style={{ width: `${labelSize}ch` }}
186 readOnly={!editable}
187 />
188 <OpenedApps apps={displayStickyWindowsSeparately ? apps : allApps} />
189 </button>
190 {SIPDisabled && <SpaceOptions index={index} setHovered={setHovered} />}
191 </div>
192 </React.Fragment>
193 );
194}
diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/spaces.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/spaces.jsx
new file mode 100755
index 0000000..e3a69d0
--- /dev/null
+++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/spaces.jsx
@@ -0,0 +1,109 @@
1import * as Uebersicht from "uebersicht";
2import Space from "./space.jsx";
3import Stickies from "./stickies.jsx";
4import * as Icons from "../icons/icons.jsx";
5import { SuspenseIcon } from "../icons/icon.jsx";
6import { useYabaiContext } from "../yabai-context.jsx";
7import { useSimpleBarContext } from "../simple-bar-context.jsx";
8import * as Utils from "../../utils";
9import * as Yabai from "../../yabai";
10
11export { spacesStyles as styles } from "../../styles/components/spaces/spaces";
12
13const { React } = Uebersicht;
14
15/**
16 * Spaces component to display spaces and manage space-related actions.
17 * @returns {JSX.Element|null} The rendered component.
18 */
19const Component = React.memo(() => {
20 // Get spaces and windows data from Yabai context
21 const { spaces, windows } = useYabaiContext();
22 // Get various settings and display information from simple-bar context
23 const { SIPDisabled, displayIndex, displays, settings } =
24 useSimpleBarContext();
25 const { spacesDisplay, process } = settings;
26 const {
27 displayStickyWindowsSeparately,
28 spacesExclusions,
29 exclusionsAsRegex,
30 hideCreateSpaceButton,
31 showOnDisplay,
32 } = spacesDisplay;
33 // Determine if the component should be visible on the current display
34 const visible = Utils.isVisibleOnDisplay(displayIndex, showOnDisplay);
35 const isProcessVisible = Utils.isVisibleOnDisplay(
36 displayIndex,
37 process.showOnDisplay,
38 );
39
40 if (!visible) return null;
41
42 if (!spaces && !windows) {
43 return <div className="spaces spaces--empty" />;
44 }
45
46 // Find the current space index
47 const { index: currentSpaceIndex } =
48 spaces.find((space) => {
49 const { "has-focus": hasFocus, focused: __legacyHasFocus } = space;
50 return hasFocus ?? __legacyHasFocus;
51 }) || {};
52
53 return displays.map((display, i) => {
54 if (display.index !== displayIndex) return null;
55
56 /**
57 * Handle click event to create a new space.
58 * @param {Event} e - The click event.
59 */
60 const onClick = async (e) => {
61 Utils.clickEffect(e);
62 await Yabai.createSpace(displayIndex);
63 };
64
65 return (
66 <div key={i} className="spaces">
67 {displayStickyWindowsSeparately && <Stickies display={display} />}
68 {spaces.map((space, i) => {
69 const { label, index } = space;
70 const lastOfSpace =
71 i !== 0 && space.display !== spaces[i - 1].display;
72
73 const key = label?.length ? label : index;
74 const spacesExclusionsList = spacesExclusions.replace(/ /g, "").split(",");
75 const isExcluded = Utils.isSpaceExcluded(
76 key,
77 spacesExclusionsList,
78 exclusionsAsRegex,
79 );
80
81 if (isExcluded) return null;
82
83 return (
84 <Space
85 key={key}
86 display={display}
87 space={space}
88 currentSpaceIndex={currentSpaceIndex}
89 lastOfSpace={lastOfSpace}
90 />
91 );
92 })}
93 {SIPDisabled && !hideCreateSpaceButton ? (
94 <button className="spaces__add" onClick={onClick}>
95 <SuspenseIcon>
96 <Icons.Add />
97 </SuspenseIcon>
98 </button>
99 ) : (
100 isProcessVisible && <div className="spaces__end-separator" />
101 )}
102 </div>
103 );
104 });
105});
106
107Component.displayName = "Spaces";
108
109export default Component;
diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/stickies.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/stickies.jsx
new file mode 100755
index 0000000..703bf68
--- /dev/null
+++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/stickies.jsx
@@ -0,0 +1,56 @@
1import OpenedApps from "./opened-apps.jsx";
2import { useYabaiContext } from "../yabai-context.jsx";
3import { useSimpleBarContext } from "../simple-bar-context.jsx";
4import * as Utils from "../../utils";
5
6/**
7 * Stickies component to display sticky windows.
8 * @param {Object} props - Component properties.
9 * @param {Object} props.display - The current display information.
10 * @returns {JSX.Element|null} The rendered component or null if no sticky windows are present.
11 */
12export default function Stickies({ display }) {
13 // Get windows from yabai context
14 const { windows } = useYabaiContext();
15 // Get settings from simple-bar context
16 const { settings } = useSimpleBarContext();
17 const { spacesDisplay } = settings;
18 const { exclusionsAsRegex, hideDuplicateAppsInSpaces } = spacesDisplay;
19
20 // Determine exclusions based on settings
21 const exclusions = exclusionsAsRegex
22 ? spacesDisplay.exclusions
23 : spacesDisplay.exclusions.split(", ");
24 const titleExclusions = exclusionsAsRegex
25 ? spacesDisplay.titleExclusions
26 : spacesDisplay.titleExclusions.split(", ");
27
28 // Get sticky windows using a utility function
29 const { stickyWindows: apps } = Utils.stickyWindowWorkaround({
30 windows,
31 uniqueApps: hideDuplicateAppsInSpaces,
32 currentDisplay: display,
33 currentSpace: undefined,
34 exclusions,
35 titleExclusions,
36 exclusionsAsRegex,
37 });
38
39 // Filter out minimized sticky windows
40 const notMinimizedStikies = apps.filter((app) => {
41 const { "is-minimized": isMinimized, minimized: __legacyIsMinimized } = app;
42 return !(isMinimized || __legacyIsMinimized);
43 });
44
45 // Return null if no non-minimized sticky windows are present
46 if (!notMinimizedStikies?.length) return null;
47
48 // Render the sticky windows
49 return (
50 <div className="stickies">
51 <div className="stickies__inner">
52 <OpenedApps apps={apps} />
53 </div>
54 </div>
55 );
56}
diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/window.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/window.jsx
new file mode 100755
index 0000000..2b47878
--- /dev/null
+++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/window.jsx
@@ -0,0 +1,119 @@
1import * as Uebersicht from "uebersicht";
2import * as AppIcons from "../../app-icons";
3import { SuspenseIcon } from "../icons/icon.jsx";
4import { useSimpleBarContext } from "../simple-bar-context.jsx";
5import * as Utils from "../../utils";
6import * as Yabai from "../../yabai";
7
8const { React } = Uebersicht;
9
10/**
11 * Window component to display a window in simple-bar.
12 * @param {Object} props - The component props.
13 * @param {Object} props.window - The window object containing window details.
14 * @returns {JSX.Element|null} The rendered window component or null if the window is minimized or not focused.
15 */
16export default function Window({ window }) {
17 const { settings } = useSimpleBarContext();
18 const ref = React.useRef();
19
20 // Destructure settings for process display options
21 const {
22 displayOnlyCurrent,
23 hideWindowTitle,
24 displayOnlyIcon,
25 expandAllProcesses,
26 displayStackIndex,
27 displayOnlyCurrentStack,
28 } = settings.process;
29
30 // Destructure window properties
31 const {
32 "stack-index": stackIndex,
33 "is-minimized": isMinimized,
34 minimized: __legacyIsMinimized,
35 "has-focus": hasFocus,
36 focused: __legacyHasFocus,
37 app: appName,
38 title,
39 id,
40 } = window;
41
42 // Determine if the window is focused
43 const isFocused = hasFocus ?? __legacyHasFocus;
44
45 // Return null if the window is minimized or not focused when displayOnlyCurrent is true
46 if (
47 (isMinimized ?? __legacyIsMinimized) ||
48 (displayOnlyCurrent && !isFocused)
49 ) {
50 return null;
51 }
52
53 // Get the icon for the application or use the default icon
54 const Icon = AppIcons.apps[appName] || AppIcons.apps.Default;
55
56 /**
57 * Handle click event on the window button.
58 * @param {Event} e - The click event.
59 */
60 const onClick = (e) => {
61 !displayOnlyCurrent && Utils.clickEffect(e);
62 Yabai.focusWindow(id);
63 };
64
65 /**
66 * Handle mouse enter event to start sliding animation.
67 */
68 const onMouseEnter = () => {
69 Utils.startSliding(ref.current, ".process__inner", ".process__name");
70 };
71
72 /**
73 * Handle mouse leave event to stop sliding animation.
74 */
75 const onMouseLeave = () => {
76 Utils.stopSliding(ref.current, ".process__name");
77 };
78
79 // Generate class names for the window button
80 const classes = Utils.classNames("process__window", {
81 "process__window--expanded": expandAllProcesses,
82 "process__window--focused": !displayOnlyCurrent && isFocused,
83 "process__window--only-current": displayOnlyCurrent,
84 "process__window--only-icon": displayOnlyIcon,
85 });
86
87 // Clean up the window name for display
88 const cleanedUpName =
89 appName !== title && title.length ? `${appName} / ${title}` : appName;
90 const processName = hideWindowTitle ? appName : cleanedUpName;
91
92 // Determine if the stack index should be displayed
93 const showStackIndex =
94 displayStackIndex &&
95 (!displayOnlyCurrentStack || isFocused) &&
96 stackIndex > 0;
97
98 return (
99 <button
100 ref={ref}
101 className={classes}
102 onClick={onClick}
103 onMouseEnter={displayOnlyIcon ? undefined : onMouseEnter}
104 onMouseLeave={displayOnlyIcon ? undefined : onMouseLeave}
105 >
106 <SuspenseIcon>
107 <Icon className="process__icon" />
108 </SuspenseIcon>
109 {!displayOnlyIcon && (
110 <span className="process__inner">
111 <span className="process__name">{processName}</span>
112 </span>
113 )}
114 {showStackIndex && (
115 <span className="process__stack-index">{stackIndex}</span>
116 )}
117 </button>
118 );
119}