From d06784958d73159b5e4abafe5114804662114ed7 Mon Sep 17 00:00:00 2001 From: Ryan Schanzenbacher Date: Thu, 9 Jul 2026 22:15:53 -0400 Subject: move ubersicht modules in tree --- .../lib/components/yabai/opened-apps.jsx | 55 ++++++ .../lib/components/yabai/process.jsx | 102 +++++++++++ .../lib/components/yabai/space-options.jsx | 84 +++++++++ .../lib/components/yabai/space.jsx | 194 +++++++++++++++++++++ .../lib/components/yabai/spaces.jsx | 109 ++++++++++++ .../lib/components/yabai/stickies.jsx | 56 ++++++ .../lib/components/yabai/window.jsx | 119 +++++++++++++ 7 files changed, 719 insertions(+) create mode 100755 users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/opened-apps.jsx create mode 100755 users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/process.jsx create mode 100755 users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/space-options.jsx create mode 100755 users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/space.jsx create mode 100755 users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/spaces.jsx create mode 100755 users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/stickies.jsx create mode 100755 users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai/window.jsx (limited to 'users/ryan/modules/simple-bar/simple-bar-source/lib/components/yabai') 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 @@ +import * as Uebersicht from "uebersicht"; +import * as AppIcons from "../../app-icons.js"; +import { SuspenseIcon } from "../icons/icon.jsx"; +import * as Utils from "../../utils.js"; + +const { React } = Uebersicht; + +/** + * OpenedApps component to display a list of opened applications. + * @param {Object} props - Component properties. + * @param {Array} props.apps - The list of opened applications. + * @returns {JSX.Element|null} The rendered component or null if no applications are present. + */ +export default function OpenedApps({ apps }) { + // Return null if no applications are present + if (!apps.length) return null; + + // Sort and map the applications to JSX elements + return Utils.sortWindows(apps).map((app, i) => { + const { + "is-minimized": isMinimized, + minimized: __legacyIsMinimized, + "has-focus": hasFocus, + focused: __legacyHasFocus, + "has-parent-zoom": hasParentZoom, + "zoom-parent": __legacyHasParentZoom, + "has-fullscreen-zoom": hasFullscreenZoom, + "zoom-fullscreen": __legacyHasFullscreenZoom, + "is-topmost": isTopmost, + } = app; + const appName = Utils.normalizeAppName(app.app); + + // Skip minimized applications + if (isMinimized ?? __legacyIsMinimized) return null; + + // Get the application icon or default icon + const Icon = AppIcons.apps[appName] || AppIcons.apps.Default; + + // Determine the CSS classes for the icon + const classes = Utils.classNames("space__icon", { + "space__icon--focused": hasFocus ?? __legacyHasFocus, + "space__icon--fullscreen": + (hasParentZoom ?? __legacyHasParentZoom) || + (hasFullscreenZoom ?? __legacyHasFullscreenZoom), + "space__icon--topmost": isTopmost, + }); + + // Render the application icon + return ( + + + + ); + }); +} 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 @@ +import * as Uebersicht from "uebersicht"; +import Window from "./window.jsx"; +import * as Utils from "../../utils"; +import { useYabaiContext } from "../yabai-context.jsx"; +import { useSimpleBarContext } from "../simple-bar-context.jsx"; + +export { processStyles as styles } from "../../styles/components/process"; + +const { React } = Uebersicht; + +/** + * Process component that displays the current windows and spaces information. + * @returns {JSX.Element|null} The rendered component or null if not visible. + */ +const Component = React.memo(() => { + // Get context values from yabai and simple-bar + const { spaces, windows, skhdMode } = useYabaiContext(); + const { displayIndex, settings } = useSimpleBarContext(); + const { process, spacesDisplay, widgets } = settings; + const { processWidget } = widgets; + const { exclusionsAsRegex } = spacesDisplay; + const { displayForFocusedSpace, centered, showCurrentSpaceMode, displaySkhdMode, showOnDisplay } = + process; + + // Determine if the process widget should be visible + const visible = + processWidget && + Utils.isVisibleOnDisplay(displayIndex, showOnDisplay) && + windows; + + if (!visible) return null; + + // Parse exclusions based on settings + const exclusions = exclusionsAsRegex + ? spacesDisplay.exclusions + : spacesDisplay.exclusions.split(", "); + + const titleExclusions = exclusionsAsRegex + ? spacesDisplay.titleExclusions + : spacesDisplay.titleExclusions.split(", "); + + // Find the current space based on visibility and display index + const currentSpace = spaces.find((space) => { + const { + "is-visible": isVisible, + "has-focus" : hasFocus, + visible: __legacyIsVisible, + display, + } = space; + return (isVisible ?? __legacyIsVisible) && (displayForFocusedSpace ? hasFocus : display === displayIndex); + }); + + // Get sticky and non-sticky windows using a utility function + const { stickyWindows, nonStickyWindows } = Utils.stickyWindowWorkaround({ + windows, + uniqueApps: false, + currentDisplay: displayIndex, + currentSpace: currentSpace?.index, + exclusions, + titleExclusions, + exclusionsAsRegex, + }); + + // Combine sticky and non-sticky windows + const apps = [...stickyWindows, ...nonStickyWindows]; + + // Determine CSS classes for the component + const classes = Utils.classNames("process", { + "process--centered": centered, + }); + + // Determine the current skhd mode and its color + const currentSkhdMode = skhdMode.mode === "default" ? null : skhdMode.mode; + const skhdModeColor = "var(--" + skhdMode.color + ")"; + + return ( +
+
+ {showCurrentSpaceMode && currentSpace && ( +
+ {currentSpace.type} +
+ )} + {displaySkhdMode && currentSkhdMode && ( +
+ {currentSkhdMode} +
+ )} + {Utils.sortWindows(apps).map((window, i) => ( + + ))} +
+
+ ); +}); + +Component.displayName = "Process"; + +export 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 @@ +import * as Uebersicht from "uebersicht"; +import * as Icons from "../icons/icons.jsx"; +import { SuspenseIcon } from "../icons/icon.jsx"; +import { useSimpleBarContext } from "../simple-bar-context.jsx"; +import * as Utils from "../../utils.js"; +import * as Yabai from "../../yabai.js"; + +const { React } = Uebersicht; + +/** + * SpaceOptions component provides options to manipulate spaces (move left, move right, remove). + * @param {Object} props - The component props. + * @param {number} props.index - The index of the space. + * @param {Function} props.setHovered - Function to set the hovered state. + * @returns {JSX.Element} The SpaceOptions component. + */ +export default function SpaceOptions({ index, setHovered }) { + // Get displayIndex from the context + const { displayIndex } = useSimpleBarContext(); + + /** + * Handles the click event to remove a space. + * @param {Event} e - The click event. + */ + const remove = async (e) => { + e.stopPropagation(); + Utils.clickEffect(e); + setHovered(false); + await Yabai.removeSpace(index, displayIndex); + }; + + /** + * Returns a function to handle the click event to swap a space. + * @param {string} direction - The direction to swap the space ("left" or "right"). + * @returns {Function} The click event handler. + */ + const moveTo = (direction) => async (e) => { + Utils.clickEffect(e); + setHovered(false); + await Yabai.swapSpace(index, direction); + }; + + /** + * Prevents the default behavior of the mouse down event. + * @param {Event} e - The mouse down event. + */ + const onMouseDown = (e) => e.preventDefault(); + + /** + * Handles the mouse leave event to unset the hovered state. + */ + const onMouseLeave = () => setHovered(false); + + return ( + +
+ + + +
+
+ + + +
+
+ + + +
+
+ ); +} 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 @@ +import * as Uebersicht from "uebersicht"; +import OpenedApps from "./opened-apps.jsx"; +import SpaceOptions from "./space-options.jsx"; +import { useYabaiContext } from "../yabai-context.jsx"; +import { useSimpleBarContext } from "../simple-bar-context.jsx"; +import * as Utils from "../../utils.js"; +import * as Yabai from "../../yabai.js"; + +const { React } = Uebersicht; + +/** + * Component representing a space in yabai window manager. + * @param {Object} props - The component props. + * @param {Object} props.space - The space object. + * @param {number} props.display - The display index. + * @param {number} props.currentSpaceIndex - The current space index. + * @param {boolean} props.lastOfSpace - Whether this is the last space. + * @returns {JSX.Element|null} The rendered component. + */ +export default function Space({ + space, + display, + currentSpaceIndex, + lastOfSpace, +}) { + const { windows } = useYabaiContext(); + const { SIPDisabled, settings } = useSimpleBarContext(); + const { spacesDisplay } = settings; + const { + displayAllSpacesOnAllScreens, + exclusionsAsRegex, + displayStickyWindowsSeparately, + hideDuplicateAppsInSpaces, + showOptionsOnHover, + } = spacesDisplay; + + const labelRef = React.useRef(); + const [hovered, setHovered] = React.useState(false); + const [noDelay, setNoDelay] = React.useState(false); + const [editable, setEditable] = React.useState(false); + const { + index, + label, + "has-focus": hasFocus, + focused: __legacyHasFocus, + "is-visible": isVisible, + visible: __legacyIsVisible, + "is-native-fullscreen": isNativeFullscreen, + "native-fullscreen": __legacyIsNativeFullscreen, + type, + } = space; + const [spaceLabel, setSpaceLabel] = React.useState( + label?.length ? label : index, + ); + + // Return null if the space should not be displayed on the current screen + if (!displayAllSpacesOnAllScreens && display.index !== space.display) + return null; + + const exclusions = exclusionsAsRegex + ? spacesDisplay.exclusions + : spacesDisplay.exclusions.split(", "); + const titleExclusions = exclusionsAsRegex + ? spacesDisplay.titleExclusions + : spacesDisplay.titleExclusions.split(", "); + + /** + * Handle mouse enter event. + * @param {MouseEvent} e - The mouse event. + */ + const onMouseEnter = (e) => { + if (!showOptionsOnHover) return; + const { altKey, metaKey } = e; + if (altKey) return; + setHovered(true); + if (metaKey) setNoDelay(true); + }; + + /** + * Handle mouse leave event. + */ + const onMouseLeave = () => { + setHovered(false); + setNoDelay(false); + setEditable(false); + window.getSelection().removeAllRanges(); + }; + + /** + * Handle click event. + * @param {MouseEvent} e - The mouse event. + */ + const onClick = (e) => { + onMouseLeave(e); + if (e.altKey) { + setEditable(true); + labelRef.current?.select(); + return; + } + if (hasFocus || __legacyHasFocus) return; + if (SIPDisabled && !spacesDisplay.switchSpacesWithoutYabai) { + Yabai.goToSpace(index); + Utils.clickEffect(e); + return; + } + Utils.switchSpace(currentSpaceIndex, index); + Utils.clickEffect(e); + }; + + /** + * Handle right-click event. + */ + const onRightClick = () => { + setHovered(true); + setNoDelay(true); + }; + + /** + * Handle change event for the space label input. + * @param {Event} e - The change event. + */ + const onChange = (e) => { + if (!editable) return; + const newLabel = e.target.value; + setSpaceLabel(newLabel); + Yabai.renameSpace(index, newLabel); + }; + + const { nonStickyWindows: apps, stickyWindows } = + Utils.stickyWindowWorkaround({ + windows, + uniqueApps: hideDuplicateAppsInSpaces, + currentDisplay: display, + currentSpace: index, + exclusions, + titleExclusions, + exclusionsAsRegex, + }); + const allApps = [...apps, ...stickyWindows]; + + // Determine if the space should be hidden + const hidden = + !(hasFocus ?? __legacyHasFocus) && + !(isVisible ?? __legacyHasFocus) && + !allApps.length && + spacesDisplay.hideEmptySpaces; + + if (hidden) return null; + + const classes = Utils.classNames(`space space--${type}`, { + "space--focused": hasFocus ?? __legacyHasFocus, + "space--visible": isVisible ?? __legacyIsVisible, + "space--fullscreen": isNativeFullscreen ?? __legacyIsNativeFullscreen, + "space--hovered": hovered, + "space--no-delay": noDelay, + "space--empty": allApps.length, + "space--editable": editable, + }); + + const labelSize = ( + typeof spaceLabel === "number" ? spaceLabel.toString() : spaceLabel + ).length; + + return ( + + {displayAllSpacesOnAllScreens && lastOfSpace && ( +
+ )} +
+ + {SIPDisabled && } +
+ + ); +} 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 @@ +import * as Uebersicht from "uebersicht"; +import Space from "./space.jsx"; +import Stickies from "./stickies.jsx"; +import * as Icons from "../icons/icons.jsx"; +import { SuspenseIcon } from "../icons/icon.jsx"; +import { useYabaiContext } from "../yabai-context.jsx"; +import { useSimpleBarContext } from "../simple-bar-context.jsx"; +import * as Utils from "../../utils"; +import * as Yabai from "../../yabai"; + +export { spacesStyles as styles } from "../../styles/components/spaces/spaces"; + +const { React } = Uebersicht; + +/** + * Spaces component to display spaces and manage space-related actions. + * @returns {JSX.Element|null} The rendered component. + */ +const Component = React.memo(() => { + // Get spaces and windows data from Yabai context + const { spaces, windows } = useYabaiContext(); + // Get various settings and display information from simple-bar context + const { SIPDisabled, displayIndex, displays, settings } = + useSimpleBarContext(); + const { spacesDisplay, process } = settings; + const { + displayStickyWindowsSeparately, + spacesExclusions, + exclusionsAsRegex, + hideCreateSpaceButton, + showOnDisplay, + } = spacesDisplay; + // Determine if the component should be visible on the current display + const visible = Utils.isVisibleOnDisplay(displayIndex, showOnDisplay); + const isProcessVisible = Utils.isVisibleOnDisplay( + displayIndex, + process.showOnDisplay, + ); + + if (!visible) return null; + + if (!spaces && !windows) { + return
; + } + + // Find the current space index + const { index: currentSpaceIndex } = + spaces.find((space) => { + const { "has-focus": hasFocus, focused: __legacyHasFocus } = space; + return hasFocus ?? __legacyHasFocus; + }) || {}; + + return displays.map((display, i) => { + if (display.index !== displayIndex) return null; + + /** + * Handle click event to create a new space. + * @param {Event} e - The click event. + */ + const onClick = async (e) => { + Utils.clickEffect(e); + await Yabai.createSpace(displayIndex); + }; + + return ( +
+ {displayStickyWindowsSeparately && } + {spaces.map((space, i) => { + const { label, index } = space; + const lastOfSpace = + i !== 0 && space.display !== spaces[i - 1].display; + + const key = label?.length ? label : index; + const spacesExclusionsList = spacesExclusions.replace(/ /g, "").split(","); + const isExcluded = Utils.isSpaceExcluded( + key, + spacesExclusionsList, + exclusionsAsRegex, + ); + + if (isExcluded) return null; + + return ( + + ); + })} + {SIPDisabled && !hideCreateSpaceButton ? ( + + ) : ( + isProcessVisible &&
+ )} +
+ ); + }); +}); + +Component.displayName = "Spaces"; + +export 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 @@ +import OpenedApps from "./opened-apps.jsx"; +import { useYabaiContext } from "../yabai-context.jsx"; +import { useSimpleBarContext } from "../simple-bar-context.jsx"; +import * as Utils from "../../utils"; + +/** + * Stickies component to display sticky windows. + * @param {Object} props - Component properties. + * @param {Object} props.display - The current display information. + * @returns {JSX.Element|null} The rendered component or null if no sticky windows are present. + */ +export default function Stickies({ display }) { + // Get windows from yabai context + const { windows } = useYabaiContext(); + // Get settings from simple-bar context + const { settings } = useSimpleBarContext(); + const { spacesDisplay } = settings; + const { exclusionsAsRegex, hideDuplicateAppsInSpaces } = spacesDisplay; + + // Determine exclusions based on settings + const exclusions = exclusionsAsRegex + ? spacesDisplay.exclusions + : spacesDisplay.exclusions.split(", "); + const titleExclusions = exclusionsAsRegex + ? spacesDisplay.titleExclusions + : spacesDisplay.titleExclusions.split(", "); + + // Get sticky windows using a utility function + const { stickyWindows: apps } = Utils.stickyWindowWorkaround({ + windows, + uniqueApps: hideDuplicateAppsInSpaces, + currentDisplay: display, + currentSpace: undefined, + exclusions, + titleExclusions, + exclusionsAsRegex, + }); + + // Filter out minimized sticky windows + const notMinimizedStikies = apps.filter((app) => { + const { "is-minimized": isMinimized, minimized: __legacyIsMinimized } = app; + return !(isMinimized || __legacyIsMinimized); + }); + + // Return null if no non-minimized sticky windows are present + if (!notMinimizedStikies?.length) return null; + + // Render the sticky windows + return ( +
+
+ +
+
+ ); +} 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 @@ +import * as Uebersicht from "uebersicht"; +import * as AppIcons from "../../app-icons"; +import { SuspenseIcon } from "../icons/icon.jsx"; +import { useSimpleBarContext } from "../simple-bar-context.jsx"; +import * as Utils from "../../utils"; +import * as Yabai from "../../yabai"; + +const { React } = Uebersicht; + +/** + * Window component to display a window in simple-bar. + * @param {Object} props - The component props. + * @param {Object} props.window - The window object containing window details. + * @returns {JSX.Element|null} The rendered window component or null if the window is minimized or not focused. + */ +export default function Window({ window }) { + const { settings } = useSimpleBarContext(); + const ref = React.useRef(); + + // Destructure settings for process display options + const { + displayOnlyCurrent, + hideWindowTitle, + displayOnlyIcon, + expandAllProcesses, + displayStackIndex, + displayOnlyCurrentStack, + } = settings.process; + + // Destructure window properties + const { + "stack-index": stackIndex, + "is-minimized": isMinimized, + minimized: __legacyIsMinimized, + "has-focus": hasFocus, + focused: __legacyHasFocus, + app: appName, + title, + id, + } = window; + + // Determine if the window is focused + const isFocused = hasFocus ?? __legacyHasFocus; + + // Return null if the window is minimized or not focused when displayOnlyCurrent is true + if ( + (isMinimized ?? __legacyIsMinimized) || + (displayOnlyCurrent && !isFocused) + ) { + return null; + } + + // Get the icon for the application or use the default icon + const Icon = AppIcons.apps[appName] || AppIcons.apps.Default; + + /** + * Handle click event on the window button. + * @param {Event} e - The click event. + */ + const onClick = (e) => { + !displayOnlyCurrent && Utils.clickEffect(e); + Yabai.focusWindow(id); + }; + + /** + * Handle mouse enter event to start sliding animation. + */ + const onMouseEnter = () => { + Utils.startSliding(ref.current, ".process__inner", ".process__name"); + }; + + /** + * Handle mouse leave event to stop sliding animation. + */ + const onMouseLeave = () => { + Utils.stopSliding(ref.current, ".process__name"); + }; + + // Generate class names for the window button + const classes = Utils.classNames("process__window", { + "process__window--expanded": expandAllProcesses, + "process__window--focused": !displayOnlyCurrent && isFocused, + "process__window--only-current": displayOnlyCurrent, + "process__window--only-icon": displayOnlyIcon, + }); + + // Clean up the window name for display + const cleanedUpName = + appName !== title && title.length ? `${appName} / ${title}` : appName; + const processName = hideWindowTitle ? appName : cleanedUpName; + + // Determine if the stack index should be displayed + const showStackIndex = + displayStackIndex && + (!displayOnlyCurrentStack || isFocused) && + stackIndex > 0; + + return ( + + ); +} -- cgit v1.3