From 9786eb8672213344d8d1b7bdef12bc94510b20db Mon Sep 17 00:00:00 2001 From: outremonde Date: Tue, 10 Jun 2025 20:32:00 -0400 Subject: initialized repository Former-commit-id: 84647f22b951a957b2b83885b612115d473f6626 --- features/user/gui/apps/foot.nix | 14 + features/user/gui/apps/librewolf.nix | 19 + features/user/gui/apps/mpv.nix | 28 + features/user/gui/apps/obs.nix | 10 + features/user/gui/apps/qutebrowser/default.nix | 81 +++ .../gui/apps/qutebrowser/scripts/yt-ad-skip.js | 29 + .../apps/qutebrowser/scripts/yt-dislike-viewer.js | 704 +++++++++++++++++++++ .../apps/qutebrowser/scripts/yt-shorts-blocker.js | 23 + .../apps/qutebrowser/scripts/yt-sponsor-skip.js | 51 ++ features/user/gui/apps/vesktop.nix | 8 + features/user/gui/bundles/video.nix | 11 + features/user/gui/desktops/niri/default.nix | 126 ++++ features/user/gui/desktops/niri/keybinds.nix | 165 +++++ features/user/gui/desktops/niri/parts/fuzzel.nix | 30 + features/user/gui/desktops/niri/parts/hyprlock.nix | 46 ++ .../user/gui/desktops/niri/parts/ignis/default.nix | 21 + features/user/gui/desktops/niri/parts/mako.nix | 26 + .../user/gui/desktops/niri/parts/selectors.nix | 117 ++++ features/user/gui/desktops/niri/parts/swww.nix | 8 + features/user/gui/desktops/niri/parts/waybar.nix | 136 ++++ features/user/gui/desktops/niri/parts/wl-kbptr.nix | 14 + features/user/gui/desktops/niri/parts/wluma.nix | 11 + features/user/gui/desktops/niri/readme.md | 9 + 23 files changed, 1687 insertions(+) create mode 100644 features/user/gui/apps/foot.nix create mode 100644 features/user/gui/apps/librewolf.nix create mode 100644 features/user/gui/apps/mpv.nix create mode 100644 features/user/gui/apps/obs.nix create mode 100644 features/user/gui/apps/qutebrowser/default.nix create mode 100644 features/user/gui/apps/qutebrowser/scripts/yt-ad-skip.js create mode 100644 features/user/gui/apps/qutebrowser/scripts/yt-dislike-viewer.js create mode 100644 features/user/gui/apps/qutebrowser/scripts/yt-shorts-blocker.js create mode 100644 features/user/gui/apps/qutebrowser/scripts/yt-sponsor-skip.js create mode 100644 features/user/gui/apps/vesktop.nix create mode 100644 features/user/gui/bundles/video.nix create mode 100755 features/user/gui/desktops/niri/default.nix create mode 100644 features/user/gui/desktops/niri/keybinds.nix create mode 100755 features/user/gui/desktops/niri/parts/fuzzel.nix create mode 100755 features/user/gui/desktops/niri/parts/hyprlock.nix create mode 100755 features/user/gui/desktops/niri/parts/ignis/default.nix create mode 100755 features/user/gui/desktops/niri/parts/mako.nix create mode 100644 features/user/gui/desktops/niri/parts/selectors.nix create mode 100755 features/user/gui/desktops/niri/parts/swww.nix create mode 100755 features/user/gui/desktops/niri/parts/waybar.nix create mode 100644 features/user/gui/desktops/niri/parts/wl-kbptr.nix create mode 100644 features/user/gui/desktops/niri/parts/wluma.nix create mode 100644 features/user/gui/desktops/niri/readme.md (limited to 'features/user/gui') diff --git a/features/user/gui/apps/foot.nix b/features/user/gui/apps/foot.nix new file mode 100644 index 0000000..d099e16 --- /dev/null +++ b/features/user/gui/apps/foot.nix @@ -0,0 +1,14 @@ +{ config, pkgs, lib, ... }: let + cfg = config.features.gui.apps.foot; +in { + options.features.gui.apps.foot.enable = lib.mkEnableOption "foot"; + config = lib.mkIf cfg.enable { + programs.foot = { + enable = true; + server.enable = true; + settings = { + main.pad = "0x4"; + }; + }; + }; +} diff --git a/features/user/gui/apps/librewolf.nix b/features/user/gui/apps/librewolf.nix new file mode 100644 index 0000000..fc6c2c4 --- /dev/null +++ b/features/user/gui/apps/librewolf.nix @@ -0,0 +1,19 @@ +{ + config, + pkgs, + lib, + ... +}: let + cfg = config.features.gui.apps.librewolf; +in { + options.features.gui.apps.librewolf.enable = lib.mkEnableOption "librewolf"; + config = lib.mkIf cfg.enable { + # programs.librewolf = { + # enable = true; + # settings = { + # "browser.tabs.inTitlebar" = 0; + # }; + # }; + home.packages = [ pkgs.librewolf-bin ]; + }; +} diff --git a/features/user/gui/apps/mpv.nix b/features/user/gui/apps/mpv.nix new file mode 100644 index 0000000..562d151 --- /dev/null +++ b/features/user/gui/apps/mpv.nix @@ -0,0 +1,28 @@ +{ config, pkgs, lib, ... }: let + cfg = config.features.gui.apps.mpv; +in { + options.features.gui.apps.mpv.enable = lib.mkEnableOption "mpv"; + config = lib.mkIf cfg.enable { + programs.mpv = { + enable = true; + config = { + # Change youtube downloader to yt-dlp for faster downloads. + script-opts = "ytdl_hook-ytdl_path=${lib.getExe pkgs.yt-dlp}"; + # Download videos at 720p or lower. + ytdl-format = "bestvideo[height<=720]+bestaudio/best[height<=720]"; + + save-position-on-quit = false; # makes mpv audio only when true? + + osd-bar = false; + }; + bindings = { + "tab" = "script-binding uosc/toggle-ui"; + }; + scripts = with pkgs.mpvScripts; [ + uosc + thumbfast + sponsorblock + ]; + }; + }; +} diff --git a/features/user/gui/apps/obs.nix b/features/user/gui/apps/obs.nix new file mode 100644 index 0000000..916f11e --- /dev/null +++ b/features/user/gui/apps/obs.nix @@ -0,0 +1,10 @@ +{ config, lib, ... }: let + cfg = config.features.gui.apps.obs; +in { + options.features.gui.apps.obs.enable = lib.mkEnableOption "obs"; + config = lib.mkIf cfg.enable { + programs.obs-studio = { + enable = true; + }; + }; +} diff --git a/features/user/gui/apps/qutebrowser/default.nix b/features/user/gui/apps/qutebrowser/default.nix new file mode 100644 index 0000000..e12c9f3 --- /dev/null +++ b/features/user/gui/apps/qutebrowser/default.nix @@ -0,0 +1,81 @@ +{ config, pkgs, lib, ... }: let + cfg = config.features.gui.apps.qutebrowser; +in { + options.features.gui.apps.qutebrowser.enable = lib.mkEnableOption "qutebrowser"; + config = lib.mkIf cfg.enable { + programs.qutebrowser = { + enable = true; + settings = { + content = { + autoplay = false; + tls.certificate_errors = "ask-block-thirdparty"; + }; + scrolling.bar = "never"; + window.transparent = true; + keyhint.delay = 0; + tabs = { + position = "left"; + width = "10%"; + }; + url = { + default_page = "https://web.tabliss.io"; + start_pages = "https://web.tabliss.io"; + }; + input.insert_mode.auto_load = true; + }; + keyBindings = { + normal = { + # Tab Movement Keys + "" = "tab-move +"; + "" = "tab-move -"; + + # Universal Scrolling Keys + "" = "scroll-px 0 50"; + "" = "scroll-px 0 -50"; + + # Open Tab Relatively + "" = "cmd-set-text -s :open -tr"; + + # Move Tab to Another Window + "gc" = "cmd-set-text -s :tab-give"; + + # Hide UI + "z" = lib.mkMerge [ + "config-cycle tabs.show never always" + "config-cycle statusbar.show in-mode always" + "config-cycle scrolling.bar never always" + ]; + }; + }; + searchEngines = { + # Default Search Engine + DEFAULT = "https://search.brave.com/search?q={}"; + + # General Search Engines + g = "https://www.google.com/search?q={}"; + b = "https://www.bing.com/search?q={}"; + + # Other Search Engines + w = "https://en.wikipedia.org/wiki/Special:Search?search={}&go=Go&ns0=1"; + y = "https://youtube.com/results?search_query={}"; + t = "https://www.wordreference.com/es/translation.asp?tranword={}"; + p = "https://thangs.com/search/{}?scope=all"; + + # Nix Search Engines + n = "https://mynixos.com/search?q={}"; + nw = "https://wiki.nixos.org/index.php?search={}"; + np = "https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query={}"; + no = "https://search.nixos.org/options?channel=unstable&size=50&sort=relevance&type=packages&query={}"; + nh = "https://home-manager-options.extranix.com/?query={}&release=master"; + }; + greasemonkey = let + mkGmScript = name: ( pkgs.writeText "${name}.js" (builtins.readFile ./scripts/${name}.js) ); + in [ + (mkGmScript "yt-ad-skip") + (mkGmScript "yt-sponsor-skip") + (mkGmScript "yt-shorts-blocker") + (mkGmScript "yt-dislike-viewer") + ]; + }; + }; +} diff --git a/features/user/gui/apps/qutebrowser/scripts/yt-ad-skip.js b/features/user/gui/apps/qutebrowser/scripts/yt-ad-skip.js new file mode 100644 index 0000000..56336df --- /dev/null +++ b/features/user/gui/apps/qutebrowser/scripts/yt-ad-skip.js @@ -0,0 +1,29 @@ +// ==UserScript== +// @name Youtube Ad Skip +// @version 0.0.7 +// @description Make Youtube more tolerable by automatically skipping ads +// @author Adcott +// @match *://*.youtube.com/* +// ==/UserScript== + +GM_addStyle(` +#player-ads, +.adDisplay, +.ad-container, +.ytd-display-ad-renderer, +.video-ads, +ytd-rich-item-renderer:has(ytd-ad-slot-renderer), +ytd-ad-slot-renderer, +#masthead-ad, +*[class^="ytd-ad-"], +#panels.ytd-watch-flexy { + display: none !important; +}`); + +document.addEventListener('load', () => { + let ad = document.querySelector('.ad-showing:has(.ytp-ad-persistent-progress-bar-container) video'); + let skipButton = document.querySelector('.ytp-ad-skip-button'); + + if (ad) ad.currentTime = 99999; + if (skipButton) skipButton.click(); +}, true); diff --git a/features/user/gui/apps/qutebrowser/scripts/yt-dislike-viewer.js b/features/user/gui/apps/qutebrowser/scripts/yt-dislike-viewer.js new file mode 100644 index 0000000..50f3eff --- /dev/null +++ b/features/user/gui/apps/qutebrowser/scripts/yt-dislike-viewer.js @@ -0,0 +1,704 @@ +// ==UserScript== +// @name Return YouTube Dislike +// @namespace https://www.returnyoutubedislike.com/ +// @homepage https://www.returnyoutubedislike.com/ +// @version 3.1.5 +// @encoding utf-8 +// @description Return of the YouTube Dislike, Based off https://www.returnyoutubedislike.com/ +// @icon https://github.com/Anarios/return-youtube-dislike/raw/main/Icons/Return%20Youtube%20Dislike%20-%20Transparent.png +// @author Anarios & JRWR +// @match *://*.youtube.com/* +// @exclude *://music.youtube.com/* +// @exclude *://*.music.youtube.com/* +// @compatible chrome +// @compatible firefox +// @compatible opera +// @compatible safari +// @compatible edge +// @grant GM.xmlHttpRequest +// @connect youtube.com +// @grant GM_addStyle +// @run-at document-end +// @downloadURL https://update.greasyfork.org/scripts/436115/Return%20YouTube%20Dislike.user.js +// @updateURL https://update.greasyfork.org/scripts/436115/Return%20YouTube%20Dislike.meta.js +// ==/UserScript== + +const extConfig = { + // BEGIN USER OPTIONS + // You may change the following variables to allowed values listed in the corresponding brackets (* means default). Keep the style and keywords intact. + showUpdatePopup: false, // [true, false*] Show a popup tab after extension update (See what's new) + disableVoteSubmission: false, // [true, false*] Disable like/dislike submission (Stops counting your likes and dislikes) + disableLogging: true, // [true*, false] Disable Logging API Response in JavaScript Console. + coloredThumbs: false, // [true, false*] Colorize thumbs (Use custom colors for thumb icons) + coloredBar: false, // [true, false*] Colorize ratio bar (Use custom colors for ratio bar) + colorTheme: "classic", // [classic*, accessible, neon] Color theme (red/green, blue/yellow, pink/cyan) + numberDisplayFormat: "compactShort", // [compactShort*, compactLong, standard] Number format (For non-English locale users, you may be able to improve appearance with a different option. Please file a feature request if your locale is not covered) + numberDisplayRoundDown: true, // [true*, false] Round down numbers (Show rounded down numbers) + tooltipPercentageMode: "none", // [none*, dash_like, dash_dislike, both, only_like, only_dislike] Mode of showing percentage in like/dislike bar tooltip. + numberDisplayReformatLikes: false, // [true, false*] Re-format like numbers (Make likes and dislikes format consistent) + rateBarEnabled: false, // [true, false*] Enables ratio bar under like/dislike buttons + // END USER OPTIONS +}; + +const LIKED_STATE = "LIKED_STATE"; +const DISLIKED_STATE = "DISLIKED_STATE"; +const NEUTRAL_STATE = "NEUTRAL_STATE"; +let previousState = 3; //1=LIKED, 2=DISLIKED, 3=NEUTRAL +let likesvalue = 0; +let dislikesvalue = 0; +let preNavigateLikeButton = null; + +let isMobile = location.hostname == "m.youtube.com"; +let isShorts = () => location.pathname.startsWith("/shorts"); +let mobileDislikes = 0; +function cLog(text, subtext = "") { + if (!extConfig.disableLogging) { + subtext = subtext.trim() === "" ? "" : `(${subtext})`; + console.log(`[Return YouTube Dislikes] ${text} ${subtext}`); + } +} + +function isInViewport(element) { + const rect = element.getBoundingClientRect(); + const height = innerHeight || document.documentElement.clientHeight; + const width = innerWidth || document.documentElement.clientWidth; + return ( + // When short (channel) is ignored, the element (like/dislike AND short itself) is + // hidden with a 0 DOMRect. In this case, consider it outside of Viewport + !(rect.top == 0 && rect.left == 0 && rect.bottom == 0 && rect.right == 0) && + rect.top >= 0 && + rect.left >= 0 && + rect.bottom <= height && + rect.right <= width + ); +} + +function getButtons() { + if (isShorts()) { + let elements = document.querySelectorAll( + isMobile ? "ytm-like-button-renderer" : "#like-button > ytd-like-button-renderer", + ); + for (let element of elements) { + if (isInViewport(element)) { + return element; + } + } + } + if (isMobile) { + return ( + document.querySelector(".slim-video-action-bar-actions .segmented-buttons") ?? + document.querySelector(".slim-video-action-bar-actions") + ); + } + if (document.getElementById("menu-container")?.offsetParent === null) { + return ( + document.querySelector("ytd-menu-renderer.ytd-watch-metadata > div") ?? + document.querySelector("ytd-menu-renderer.ytd-video-primary-info-renderer > div") + ); + } else { + return document.getElementById("menu-container")?.querySelector("#top-level-buttons-computed"); + } +} + +function getDislikeButton() { + if (getButtons().children[0].tagName === "YTD-SEGMENTED-LIKE-DISLIKE-BUTTON-RENDERER") { + if (getButtons().children[0].children[1] === undefined) { + return document.querySelector("#segmented-dislike-button"); + } else { + return getButtons().children[0].children[1]; + } + } else { + if (getButtons().querySelector("segmented-like-dislike-button-view-model")) { + const dislikeViewModel = getButtons().querySelector("dislike-button-view-model"); + if (!dislikeViewModel) cLog("Dislike button wasn't added to DOM yet..."); + return dislikeViewModel; + } else { + return getButtons().children[1]; + } + } +} + +function getLikeButton() { + return getButtons().children[0].tagName === "YTD-SEGMENTED-LIKE-DISLIKE-BUTTON-RENDERER" + ? document.querySelector("#segmented-like-button") !== null + ? document.querySelector("#segmented-like-button") + : getButtons().children[0].children[0] + : getButtons().querySelector("like-button-view-model") ?? getButtons().children[0]; +} + +function getLikeTextContainer() { + return ( + getLikeButton().querySelector("#text") ?? + getLikeButton().getElementsByTagName("yt-formatted-string")[0] ?? + getLikeButton().querySelector("span[role='text']") + ); +} + +function getDislikeTextContainer() { + const dislikeButton = getDislikeButton(); + let result = + dislikeButton?.querySelector("#text") ?? + dislikeButton?.getElementsByTagName("yt-formatted-string")[0] ?? + dislikeButton?.querySelector("span[role='text']"); + if (result === null) { + let textSpan = document.createElement("span"); + textSpan.id = "text"; + textSpan.style.marginLeft = "6px"; + dislikeButton?.querySelector("button").appendChild(textSpan); + if (dislikeButton) dislikeButton.querySelector("button").style.width = "auto"; + result = textSpan; + } + return result; +} + +function createObserver(options, callback) { + const observerWrapper = new Object(); + observerWrapper.options = options; + observerWrapper.observer = new MutationObserver(callback); + observerWrapper.observe = function (element) { + this.observer.observe(element, this.options); + }; + observerWrapper.disconnect = function () { + this.observer.disconnect(); + }; + return observerWrapper; +} + +let shortsObserver = null; + +if (isShorts() && !shortsObserver) { + cLog("Initializing shorts mutation observer"); + shortsObserver = createObserver( + { + attributes: true, + }, + (mutationList) => { + mutationList.forEach((mutation) => { + if ( + mutation.type === "attributes" && + mutation.target.nodeName === "TP-YT-PAPER-BUTTON" && + mutation.target.id === "button" + ) { + cLog("Short thumb button status changed"); + if (mutation.target.getAttribute("aria-pressed") === "true") { + mutation.target.style.color = + mutation.target.parentElement.parentElement.id === "like-button" + ? getColorFromTheme(true) + : getColorFromTheme(false); + } else { + mutation.target.style.color = "unset"; + } + return; + } + cLog("Unexpected mutation observer event: " + mutation.target + mutation.type); + }); + }, + ); +} + +function isVideoLiked() { + if (isMobile) { + return getLikeButton().querySelector("button").getAttribute("aria-label") == "true"; + } + return getLikeButton().classList.contains("style-default-active"); +} + +function isVideoDisliked() { + if (isMobile) { + return getDislikeButton()?.querySelector("button").getAttribute("aria-label") == "true"; + } + return getDislikeButton()?.classList.contains("style-default-active"); +} + +function isVideoNotLiked() { + if (isMobile) { + return !isVideoLiked(); + } + return getLikeButton().classList.contains("style-text"); +} + +function isVideoNotDisliked() { + if (isMobile) { + return !isVideoDisliked(); + } + return getDislikeButton()?.classList.contains("style-text"); +} + +function checkForUserAvatarButton() { + if (isMobile) { + return; + } + if (document.querySelector("#avatar-btn")) { + return true; + } else { + return false; + } +} + +function getState() { + if (isVideoLiked()) { + return LIKED_STATE; + } + if (isVideoDisliked()) { + return DISLIKED_STATE; + } + return NEUTRAL_STATE; +} + +function setLikes(likesCount) { + if (isMobile) { + getButtons().children[0].querySelector(".button-renderer-text").innerText = likesCount; + return; + } + getLikeTextContainer().innerText = likesCount; +} + +function setDislikes(dislikesCount) { + if (isMobile) { + mobileDislikes = dislikesCount; + return; + } + + const _container = getDislikeTextContainer(); + _container?.removeAttribute("is-empty"); + if (_container?.innerText !== dislikesCount) { + _container.innerText = dislikesCount; + } +} + +function getLikeCountFromButton() { + try { + if (isShorts()) { + //Youtube Shorts don't work with this query. It's not necessary; we can skip it and still see the results. + //It should be possible to fix this function, but it's not critical to showing the dislike count. + return false; + } + let likeButton = + getLikeButton().querySelector("yt-formatted-string#text") ?? getLikeButton().querySelector("button"); + + let likesStr = likeButton.getAttribute("aria-label").replace(/\D/g, ""); + return likesStr.length > 0 ? parseInt(likesStr) : false; + } catch { + return false; + } +} + +(typeof GM_addStyle != "undefined" + ? GM_addStyle + : (styles) => { + let styleNode = document.createElement("style"); + styleNode.type = "text/css"; + styleNode.innerText = styles; + document.head.appendChild(styleNode); + })(` + #return-youtube-dislike-bar-container { + background: var(--yt-spec-icon-disabled); + border-radius: 2px; + } + + #return-youtube-dislike-bar { + background: var(--yt-spec-text-primary); + border-radius: 2px; + transition: all 0.15s ease-in-out; + } + + .ryd-tooltip { + position: absolute; + display: block; + height: 2px; + bottom: -10px; + } + + .ryd-tooltip-bar-container { + width: 100%; + height: 2px; + position: absolute; + padding-top: 6px; + padding-bottom: 12px; + top: -6px; + } + + ytd-menu-renderer.ytd-watch-metadata { + overflow-y: visible !important; + } + + #top-level-buttons-computed { + position: relative !important; + } + `); + +function createRateBar(likes, dislikes) { + if (isMobile || !extConfig.rateBarEnabled) { + return; + } + let rateBar = document.getElementById("return-youtube-dislike-bar-container"); + + const widthPx = getLikeButton().clientWidth + (getDislikeButton()?.clientWidth ?? 52); + + const widthPercent = likes + dislikes > 0 ? (likes / (likes + dislikes)) * 100 : 50; + + var likePercentage = parseFloat(widthPercent.toFixed(1)); + const dislikePercentage = (100 - likePercentage).toLocaleString(); + likePercentage = likePercentage.toLocaleString(); + + var tooltipInnerHTML; + switch (extConfig.tooltipPercentageMode) { + case "dash_like": + tooltipInnerHTML = `${likes.toLocaleString()} / ${dislikes.toLocaleString()}  -  ${likePercentage}%`; + break; + case "dash_dislike": + tooltipInnerHTML = `${likes.toLocaleString()} / ${dislikes.toLocaleString()}  -  ${dislikePercentage}%`; + break; + case "both": + tooltipInnerHTML = `${likePercentage}% / ${dislikePercentage}%`; + break; + case "only_like": + tooltipInnerHTML = `${likePercentage}%`; + break; + case "only_dislike": + tooltipInnerHTML = `${dislikePercentage}%`; + break; + default: + tooltipInnerHTML = `${likes.toLocaleString()} / ${dislikes.toLocaleString()}`; + } + + if (!rateBar && !isMobile) { + let colorLikeStyle = ""; + let colorDislikeStyle = ""; + if (extConfig.coloredBar) { + colorLikeStyle = "; background-color: " + getColorFromTheme(true); + colorDislikeStyle = "; background-color: " + getColorFromTheme(false); + } + + getButtons().insertAdjacentHTML( + "beforeend", + ` +
+
+
+
+
+
+ + ${tooltipInnerHTML} + +
+`, + ); + let descriptionAndActionsElement = document.getElementById("top-row"); + descriptionAndActionsElement.style.borderBottom = "1px solid var(--yt-spec-10-percent-layer)"; + descriptionAndActionsElement.style.paddingBottom = "10px"; + } else { + document.querySelector(".ryd-tooltip").style.width = widthPx + "px"; + document.getElementById("return-youtube-dislike-bar").style.width = widthPercent + "%"; + + if (extConfig.coloredBar) { + document.getElementById("return-youtube-dislike-bar-container").style.backgroundColor = getColorFromTheme(false); + document.getElementById("return-youtube-dislike-bar").style.backgroundColor = getColorFromTheme(true); + } + } +} + +function setState() { + cLog("Fetching votes..."); + let statsSet = false; + + fetch(`https://returnyoutubedislikeapi.com/votes?videoId=${getVideoId()}`).then((response) => { + response.json().then((json) => { + if (json && !("traceId" in response) && !statsSet) { + const { dislikes, likes } = json; + cLog(`Received count: ${dislikes}`); + likesvalue = likes; + dislikesvalue = dislikes; + setDislikes(numberFormat(dislikes)); + if (extConfig.numberDisplayReformatLikes === true) { + const nativeLikes = getLikeCountFromButton(); + if (nativeLikes !== false) { + setLikes(numberFormat(nativeLikes)); + } + } + createRateBar(likes, dislikes); + if (extConfig.coloredThumbs === true) { + const dislikeButton = getDislikeButton(); + if (isShorts()) { + // for shorts, leave deactived buttons in default color + const shortLikeButton = getLikeButton().querySelector("tp-yt-paper-button#button"); + const shortDislikeButton = dislikeButton?.querySelector("tp-yt-paper-button#button"); + if (shortLikeButton.getAttribute("aria-pressed") === "true") { + shortLikeButton.style.color = getColorFromTheme(true); + } + if (shortDislikeButton && shortDislikeButton.getAttribute("aria-pressed") === "true") { + shortDislikeButton.style.color = getColorFromTheme(false); + } + shortsObserver.observe(shortLikeButton); + shortsObserver.observe(shortDislikeButton); + } else { + getLikeButton().style.color = getColorFromTheme(true); + if (dislikeButton) dislikeButton.style.color = getColorFromTheme(false); + } + } + } + }); + }); +} + +function updateDOMDislikes() { + setDislikes(numberFormat(dislikesvalue)); + createRateBar(likesvalue, dislikesvalue); +} + +function likeClicked() { + if (checkForUserAvatarButton() == true) { + if (previousState == 1) { + likesvalue--; + updateDOMDislikes(); + previousState = 3; + } else if (previousState == 2) { + likesvalue++; + dislikesvalue--; + updateDOMDislikes(); + previousState = 1; + } else if (previousState == 3) { + likesvalue++; + updateDOMDislikes(); + previousState = 1; + } + if (extConfig.numberDisplayReformatLikes === true) { + const nativeLikes = getLikeCountFromButton(); + if (nativeLikes !== false) { + setLikes(numberFormat(nativeLikes)); + } + } + } +} + +function dislikeClicked() { + if (checkForUserAvatarButton() == true) { + if (previousState == 3) { + dislikesvalue++; + updateDOMDislikes(); + previousState = 2; + } else if (previousState == 2) { + dislikesvalue--; + updateDOMDislikes(); + previousState = 3; + } else if (previousState == 1) { + likesvalue--; + dislikesvalue++; + updateDOMDislikes(); + previousState = 2; + if (extConfig.numberDisplayReformatLikes === true) { + const nativeLikes = getLikeCountFromButton(); + if (nativeLikes !== false) { + setLikes(numberFormat(nativeLikes)); + } + } + } + } +} + +function setInitialState() { + setState(); +} + +function getVideoId() { + const urlObject = new URL(window.location.href); + const pathname = urlObject.pathname; + if (pathname.startsWith("/clip")) { + return (document.querySelector("meta[itemprop='videoId']") || document.querySelector("meta[itemprop='identifier']")).content; + } else { + if (pathname.startsWith("/shorts")) { + return pathname.slice(8); + } + return urlObject.searchParams.get("v"); + } +} + +function isVideoLoaded() { + if (isMobile) { + return document.getElementById("player").getAttribute("loading") == "false"; + } + const videoId = getVideoId(); + + return ( + // desktop: spring 2024 UI + document.querySelector(`ytd-watch-grid[video-id='${videoId}']`) !== null || + // desktop: older UI + document.querySelector(`ytd-watch-flexy[video-id='${videoId}']`) !== null || + // mobile: no video-id attribute + document.querySelector('#player[loading="false"]:not([hidden])') !== null + ); +} + +function roundDown(num) { + if (num < 1000) return num; + const int = Math.floor(Math.log10(num) - 2); + const decimal = int + (int % 3 ? 1 : 0); + const value = Math.floor(num / 10 ** decimal); + return value * 10 ** decimal; +} + +function numberFormat(numberState) { + let numberDisplay; + if (extConfig.numberDisplayRoundDown === false) { + numberDisplay = numberState; + } else { + numberDisplay = roundDown(numberState); + } + return getNumberFormatter(extConfig.numberDisplayFormat).format(numberDisplay); +} + +function getNumberFormatter(optionSelect) { + let userLocales; + if (document.documentElement.lang) { + userLocales = document.documentElement.lang; + } else if (navigator.language) { + userLocales = navigator.language; + } else { + try { + userLocales = new URL( + Array.from(document.querySelectorAll("head > link[rel='search']")) + ?.find((n) => n?.getAttribute("href")?.includes("?locale=")) + ?.getAttribute("href"), + )?.searchParams?.get("locale"); + } catch { + cLog("Cannot find browser locale. Use en as default for number formatting."); + userLocales = "en"; + } + } + + let formatterNotation; + let formatterCompactDisplay; + switch (optionSelect) { + case "compactLong": + formatterNotation = "compact"; + formatterCompactDisplay = "long"; + break; + case "standard": + formatterNotation = "standard"; + formatterCompactDisplay = "short"; + break; + case "compactShort": + default: + formatterNotation = "compact"; + formatterCompactDisplay = "short"; + } + + const formatter = Intl.NumberFormat(userLocales, { + notation: formatterNotation, + compactDisplay: formatterCompactDisplay, + }); + return formatter; +} + +function getColorFromTheme(voteIsLike) { + let colorString; + switch (extConfig.colorTheme) { + case "accessible": + if (voteIsLike === true) { + colorString = "dodgerblue"; + } else { + colorString = "gold"; + } + break; + case "neon": + if (voteIsLike === true) { + colorString = "aqua"; + } else { + colorString = "magenta"; + } + break; + case "classic": + default: + if (voteIsLike === true) { + colorString = "lime"; + } else { + colorString = "red"; + } + } + return colorString; +} + +let smartimationObserver = null; + +function setEventListeners(evt) { + let jsInitChecktimer; + + function checkForJS_Finish() { + //console.log(); + if (isShorts() || (getButtons()?.offsetParent && isVideoLoaded())) { + const buttons = getButtons(); + const dislikeButton = getDislikeButton(); + + if (preNavigateLikeButton !== getLikeButton() && dislikeButton) { + cLog("Registering button listeners..."); + try { + getLikeButton().addEventListener("click", likeClicked); + dislikeButton?.addEventListener("click", dislikeClicked); + getLikeButton().addEventListener("touchstart", likeClicked); + dislikeButton?.addEventListener("touchstart", dislikeClicked); + dislikeButton?.addEventListener("focusin", updateDOMDislikes); + dislikeButton?.addEventListener("focusout", updateDOMDislikes); + preNavigateLikeButton = getLikeButton(); + + if (!smartimationObserver) { + smartimationObserver = createObserver( + { + attributes: true, + subtree: true, + childList: true, + }, + updateDOMDislikes, + ); + smartimationObserver.container = null; + } + + const smartimationContainer = buttons.querySelector("yt-smartimation"); + if (smartimationContainer && smartimationObserver.container != smartimationContainer) { + cLog("Initializing smartimation mutation observer"); + smartimationObserver.disconnect(); + smartimationObserver.observe(smartimationContainer); + smartimationObserver.container = smartimationContainer; + } + } catch { + return; + } //Don't spam errors into the console + } + if (dislikeButton) { + setInitialState(); + clearInterval(jsInitChecktimer); + } + } + } + + cLog("Setting up..."); + jsInitChecktimer = setInterval(checkForJS_Finish, 111); +} + +(function () { + "use strict"; + window.addEventListener("yt-navigate-finish", setEventListeners, true); + setEventListeners(); +})(); +if (isMobile) { + let originalPush = history.pushState; + history.pushState = function (...args) { + window.returnDislikeButtonlistenersSet = false; + setEventListeners(args[2]); + return originalPush.apply(history, args); + }; + setInterval(() => { + const dislikeButton = getDislikeButton(); + if (dislikeButton?.querySelector(".button-renderer-text") === null) { + getDislikeTextContainer().innerText = mobileDislikes; + } else { + if (dislikeButton) dislikeButton.querySelector(".button-renderer-text").innerText = mobileDislikes; + } + }, 1000); +} diff --git a/features/user/gui/apps/qutebrowser/scripts/yt-shorts-blocker.js b/features/user/gui/apps/qutebrowser/scripts/yt-shorts-blocker.js new file mode 100644 index 0000000..383fec3 --- /dev/null +++ b/features/user/gui/apps/qutebrowser/scripts/yt-shorts-blocker.js @@ -0,0 +1,23 @@ +// ==UserScript== +// @name YouTube Shorts Blocker +// @namespace http://tampermonkey.net/ +// @version 0.1.2 +// @description Blocks the YouTube shorts from appearing. +// @author Aiden Charles +// @license MIT +// @match https://www.youtube.com/* +// @require https://code.jquery.com/jquery-3.4.1.slim.min.js +// @grant none +// ==/UserScript== + +(function() { + console.log("YouTube Shorts blocker script is running!"); + + setInterval(function() { + $("ytd-reel-shelf-renderer").hide(); + $("a[title='Shorts']").hide(); + $('a[href^="/shorts/"]').closest('ytd-video-renderer').hide(); + $('span:contains("Shorts")').closest('#content.ytd-rich-section-renderer').hide(); + }, 1000); +})(); + diff --git a/features/user/gui/apps/qutebrowser/scripts/yt-sponsor-skip.js b/features/user/gui/apps/qutebrowser/scripts/yt-sponsor-skip.js new file mode 100644 index 0000000..3779cbf --- /dev/null +++ b/features/user/gui/apps/qutebrowser/scripts/yt-sponsor-skip.js @@ -0,0 +1,51 @@ +// ==UserScript== +// @name Sponsorblock +// @version 1.1.0 +// @description Skip sponsor segments automatically +// @author afreakk +// @author vongaisberg +// @match *://*.youtube.com/* +// @exclude *://*.youtube.com/subscribe_embed?* +// ==/UserScript== +const delay = 1000; + +const tryFetchSkipSegments = (videoID) => + + fetch(`https://sponsor.ajay.app/api/skipSegments?videoID=${videoID}`) + .then((r) => r.json()) + .then((rJson) => + rJson.filter((a) => a.actionType === 'skip').map((a) => a.segment) + ) + .catch( + (e) => + console.log( + `Sponsorblock: failed fetching skipSegments for ${videoID}, reason: ${e}` + ) || [] + ); + +const skipSegments = async () => { + const videoID = new URL(document.location).searchParams.get('v'); + if (!videoID) { + return; + } + const key = `segmentsToSkip-${videoID}`; + window[key] = window[key] || (await tryFetchSkipSegments(videoID)); + for (const v of document.querySelectorAll('video')) { + if (Number.isNaN(v.duration)) continue; + for (const [start, end] of window[key]) { + if (v.currentTime < end && v.currentTime >= start) { + console.log(`Sponsorblock: skipped video @${v.currentTime} from ${start} to ${end}`); + v.currentTime = end; + return + } + const timeToSponsor = (start - v.currentTime) / v.playbackRate; + if (v.currentTime < start && timeToSponsor < (delay / 1000)) { + console.log(`Sponsorblock: Almost at sponsor segment, sleep for ${timeToSponsor * 1000}ms`); + setTimeout(skipSegments, timeToSponsor * 1000); + } + } + } +}; +if (!window.skipSegmentsIntervalID) { + window.skipSegmentsIntervalID = setInterval(skipSegments, delay); +} diff --git a/features/user/gui/apps/vesktop.nix b/features/user/gui/apps/vesktop.nix new file mode 100644 index 0000000..f28d719 --- /dev/null +++ b/features/user/gui/apps/vesktop.nix @@ -0,0 +1,8 @@ +{ config, pkgs, lib, ... }: let + cfg = config.features.gui.apps.vesktop; +in { + options.features.gui.apps.vesktop.enable = lib.mkEnableOption "vesktop"; + config = lib.mkIf cfg.enable { + home.packages = with pkgs; [(vesktop.override { withSystemVencord = false; })]; + }; +} diff --git a/features/user/gui/bundles/video.nix b/features/user/gui/bundles/video.nix new file mode 100644 index 0000000..491bf21 --- /dev/null +++ b/features/user/gui/bundles/video.nix @@ -0,0 +1,11 @@ +{ config, pkgs, lib, ... }: let + cfg = config.features.gui.bundles.video; +in { + options.features.gui.bundles.video.enable = lib.mkEnableOption "video"; + config = lib.mkIf cfg.enable { + home.packages = with pkgs; [ + kdePackages.kdenlive + ]; + features.gui.apps.obs.enable = true; + }; +} diff --git a/features/user/gui/desktops/niri/default.nix b/features/user/gui/desktops/niri/default.nix new file mode 100755 index 0000000..cd904de --- /dev/null +++ b/features/user/gui/desktops/niri/default.nix @@ -0,0 +1,126 @@ +{ + config, + pkgs, + lib, + inputs, + ... +}: let + cfg = config.features.gui.desktops.niri; + aes = config.aesthetics; +in { + imports = [ + inputs.niri.homeModules.niri + ./keybinds.nix + ]; + options.features.gui.desktops.niri.enable = lib.mkEnableOption "niri"; + config = lib.mkIf cfg.enable { + features.gui.desktops.niri.parts = { + waybar.enable = true; + fuzzel.enable = true; + selectors.enable = true; + swww.enable = true; + mako.enable = true; + ignis.enable = true; + hyprlock.enable = true; + }; + programs.niri = { + enable = true; + package = inputs.niri.packages.${pkgs.system}.niri-unstable; + settings = { + outputs = { + "Samsung Electric Company SAMSUNG 0x00000001" = { + enable = true; + scale = 1.5; + }; + }; + spawn-at-startup = [ + # Status Bar + {command = ["waybar"];} + + # Wallpaper Daemon + {command = ["swww-daemon"];} + + # Allows x apps to be used in wayland. + {command = ["${lib.getExe pkgs.xwayland-satellite}"];} + + # Logs the clipboard for use in utilities. + {command = ["${pkgs.wl-clipboard}/bin/wl-paste" "--watch" "${pkgs.cliphist}/bin/cliphist" "store"];} + ]; + environment = { + DISPLAY = ":0"; # Important for Xwayland. + }; + window-rules = [ + { + geometry-corner-radius = let + radius = 4.0; + in { + top-left = radius; + top-right = radius; + bottom-left = radius; + bottom-right = radius; + }; + clip-to-geometry = true; + default-column-width.proportion = 1. / 3.; + } + { + # Prevent Tor from being screen captured. + matches = [{app-id = "Tor Browser";}]; + block-out-from = "screen-capture"; + } + ]; + switch-events = { + lid-close.action.spawn = ["hyprlock"]; + }; + prefer-no-csd = true; + overview = { + backdrop-color = "#${aes.scheme.base01}"; + }; + layout = { + gaps = 14; + insert-hint.enable = false; + shadow = { + enable = true; + softness = 10; + spread = 5; + offset = { + x = 0; + y = 0; + }; + }; + focus-ring = { + enable = true; + width = 3; + active.color = "#${aes.scheme.base09}"; + }; + border = { + enable = false; + width = 3; + inactive.color = "#${aes.scheme.base03}"; + active.color = "#${aes.scheme.base08}"; + }; + struts = { + # left = -1; + # right = -1; + + left = 20; + right = 20; + top = 20; + bottom = 20; + }; + always-center-single-column = false; + empty-workspace-above-first = true; + }; + input.keyboard.xkb.options = '' + caps:escape, + compose:ins + ''; + hotkey-overlay.skip-at-startup = true; + input = { + touchpad = { + click-method = "clickfinger"; + }; + }; + }; + }; + }; +} diff --git a/features/user/gui/desktops/niri/keybinds.nix b/features/user/gui/desktops/niri/keybinds.nix new file mode 100644 index 0000000..5426ee6 --- /dev/null +++ b/features/user/gui/desktops/niri/keybinds.nix @@ -0,0 +1,165 @@ +{ + config, + pkgs, + lib, + ... +}: { + config = lib.mkIf config.features.gui.desktops.niri.enable { + programs.niri.settings.binds = let + left = "h"; + down = "j"; + up = "k"; + right = "l"; + in { + # App Launching Keys + "Mod+Q".action.spawn = ["footclient"]; + "Mod+W".action.spawn = ["qutebrowser"]; + "Mod+Shift+W".action.spawn = ["librewolf"]; + "Mod+E".action.spawn = ["neovide"]; + + # Clear Notifications + "Mod+B".action.spawn = ["makoctl" "dismiss" "-a"]; + + # Selectors + "Mod+R".action.spawn = ["fuzzel"]; + "Mod+T".action.spawn = ["tool-selector"]; + "Mod+Y".action.spawn = ["clipboard-selector"]; + "Mod+U".action.spawn = ["wallpaper-selector" "--all-outputs"]; + "Mod+Shift+U".action.spawn = ["wallpaper-selector"]; + "Mod+Control+U".action.spawn = ["wallpaper-selector" "--randomize" "--all-outputs"]; + "Mod+Control+Shift+U".action.spawn = ["wallpaper-selector" "--randomize"]; + + # Screenshot Keys + "Mod+P".action.screenshot = []; + "Shift+Mod+P".action.screenshot-screen = []; + "Control+Mod+P".action.screenshot-window = []; + + # Power Keys + "Mod+comma".action.spawn = ["${lib.getExe (pkgs.writers.writeNuBin "nirilock" "systemctl suspend ; hyprlock")}"]; + + # Horizontal Tiling Keys + "Mod+A".action.maximize-column = []; + "Mod+S".action.switch-preset-column-width = []; + + # Vertical Tiling Keys + "Mod+Shift+A".action.reset-window-height = []; + "Mod+Shift+S".action.switch-preset-window-height = []; + "Mod+D".action.consume-or-expel-window-right = []; + + # Floating Window Management Keys + "Mod+Z".action.switch-focus-between-floating-and-tiling = []; + "Mod+X".action.toggle-window-floating = []; + + # Other Window Management Keys + "Mod+C".action.close-window = []; + "Mod+V".action.fullscreen-window = []; + + # Overlay Keys + "Mod+F".action.toggle-overview = []; + + # +---------------------+ + # | Arrow Movement Keys | + # +---------------------+ + + # Window Focus Keys + "Mod+Left".action.focus-column-left = []; + "Mod+Right".action.focus-column-right = []; + "Mod+Up".action.focus-window-up = []; + "Mod+Down".action.focus-window-down = []; + + # Monitor Focus Keys + "Mod+Shift+Left".action.focus-monitor-left = []; + "Mod+Shift+Right".action.focus-monitor-right = []; + + # Workspace Focus Keys + "Mod+Shift+Up".action.focus-workspace-up = []; + "Mod+Shift+Down".action.focus-workspace-down = []; + + # Window Motion Keys + "Mod+Control+Left".action.move-column-left = []; + "Mod+Control+Right".action.move-column-right = []; + "Mod+Control+Up".action.move-window-up = []; + "Mod+Control+Down".action.move-window-down = []; + + # Window - Monitor Motion Keys + "Mod+Control+Shift+Left".action.move-column-to-monitor-left = []; + "Mod+Control+Shift+Right".action.move-column-to-monitor-right = []; + + # Window - Workspace Motion Keys + "Mod+Control+Shift+Up".action.move-window-to-workspace-up = []; + "Mod+Control+Shift+Down".action.move-window-to-workspace-down = []; + + # Workspace Motion Keys + "Mod+Alt+Shift+Up".action.move-workspace-up = []; + "Mod+Alt+Shift+Down".action.move-workspace-down = []; + + # Workspace - Monitor Motion Keys + "Mod+Alt+Shift+Left".action.move-workspace-to-monitor-left = []; + "Mod+Alt+Shift+Right".action.move-workspace-to-monitor-right = []; + + # +-------------------+ + # | Vim Movement Keys | + # +-------------------+ + + # Window Focus Keys + "Mod+${left}".action.focus-column-left = []; + "Mod+${right}".action.focus-column-right = []; + "Mod+${up}".action.focus-window-up = []; + "Mod+${down}".action.focus-window-down = []; + + # Monitor Focus Keys + "Mod+Shift+${left}".action.focus-monitor-left = []; + "Mod+Shift+${right}".action.focus-monitor-right = []; + + # Workspace Focus Keys + "Mod+Shift+${up}".action.focus-workspace-up = []; + "Mod+Shift+${down}".action.focus-workspace-down = []; + + # Monitor Motion Keys + "Mod+Control+Shift+${left}".action.move-column-to-monitor-left = []; + "Mod+Control+Shift+${right}".action.move-column-to-monitor-right = []; + + # Workspace Motion Keys + "Mod+Control+Shift+${up}".action.move-window-to-workspace-up = []; + "Mod+Control+Shift+${down}".action.move-window-to-workspace-down = []; + + # Window Motion Keys + "Mod+Control+${left}".action.move-column-left = []; + "Mod+Control+${right}".action.move-column-right = []; + "Mod+Control+${up}".action.move-window-up = []; + "Mod+Control+${down}".action.move-window-down = []; + + # Workspace Motion Keys + "Mod+Alt+Shift+${up}".action.move-workspace-up = []; + "Mod+Alt+Shift+${down}".action.move-workspace-down = []; + + # Workspace - Monitor Motion Keys + "Mod+Alt+Shift+${left}".action.move-workspace-to-monitor-left = []; + "Mod+Alt+Shift+${right}".action.move-workspace-to-monitor-right = []; + + # +-------------------+ + + # Numbered Workspace Movement Keys + "Mod+1".action.focus-workspace = 1; + "Mod+2".action.focus-workspace = 2; + "Mod+3".action.focus-workspace = 3; + "Mod+4".action.focus-workspace = 4; + "Mod+5".action.focus-workspace = 5; + "Mod+6".action.focus-workspace = 6; + "Mod+7".action.focus-workspace = 7; + "Mod+8".action.focus-workspace = 8; + "Mod+9".action.focus-workspace = 9; + "Mod+0".action.focus-workspace = 0; + + # XF86 Keys + "XF86AudioRaiseVolume".action.spawn = ["wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "5%+"]; + "XF86AudioLowerVolume".action.spawn = ["wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "5%-"]; + "XF86AudioMute".action.spawn = ["wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"]; + + "XF86MonBrightnessUp".action.spawn = ["${lib.getExe pkgs.brightnessctl}" "s" "+5%"]; + "XF86MonBrightnessDown".action.spawn = ["${lib.getExe pkgs.brightnessctl}" "s" "5%-"]; + + "XF86LaunchB".action.spawn = ["fuzzel"]; + }; + }; +} diff --git a/features/user/gui/desktops/niri/parts/fuzzel.nix b/features/user/gui/desktops/niri/parts/fuzzel.nix new file mode 100755 index 0000000..e939f88 --- /dev/null +++ b/features/user/gui/desktops/niri/parts/fuzzel.nix @@ -0,0 +1,30 @@ +{ config, lib, ... }: let + cfg = config.features.gui.desktops.niri.parts.fuzzel; + aes = config.aesthetics; +in { + options.features.gui.desktops.niri.parts.fuzzel.enable = lib.mkEnableOption "fuzzel"; + config = lib.mkIf cfg.enable { + programs.fuzzel = { + enable = true; + settings = { + main = { + width = 20; + #terminal = config.custom.libraries.default-applications.terminal-emulator.command; + }; + border = { + width = 3; + radius = 4; + }; + colors = with aes.scheme; { + background = "${base00}ff"; + selection = "${base00}ff"; + selection-text = "${base0C}ff"; + selection-match = "${base0E}ff"; + match = "${base0E}ff"; + border = "${base0C}ff"; + text = "${base05}ff"; + }; + }; + }; + }; +} diff --git a/features/user/gui/desktops/niri/parts/hyprlock.nix b/features/user/gui/desktops/niri/parts/hyprlock.nix new file mode 100755 index 0000000..f8a50e3 --- /dev/null +++ b/features/user/gui/desktops/niri/parts/hyprlock.nix @@ -0,0 +1,46 @@ +{ config, lib, pkgs, ... }: let + cfg = config.features.gui.desktops.niri.parts.hyprlock; +in { + options.features.gui.desktops.niri.parts.hyprlock.enable = lib.mkEnableOption "hyprlock"; + config = lib.mkIf cfg.enable { + home.packages = [( + pkgs.writers.writeNuBin "nirilock" /*nu*/ '' + niri msg action do-screen-transition --delay-ms 500 + systemctl suspend + hyprlock + '' + )]; + programs.niri.settings.window-rules = [ + { + matches = [{ title = "hyprlock"; }]; + draw-border-with-background = false; + } + ]; + programs.hyprlock = { + enable = true; + settings = { + background = { + monitor = ""; + path = "${config.aesthetics.wallpaper}"; + blur_passes = 0; + blur_size = 5; + }; + label = { + text = "$TIME"; + font_size = 65; + font_family = "Cantarell Bold"; + + position = "0, 0"; + halign = "center"; + valign = "center"; + }; + input-field = { + size = "250, 50"; + position = "0, -80"; + outline_thickness = 0; + placeholder_text = ""; + }; + }; + }; + }; +} diff --git a/features/user/gui/desktops/niri/parts/ignis/default.nix b/features/user/gui/desktops/niri/parts/ignis/default.nix new file mode 100755 index 0000000..c609ca9 --- /dev/null +++ b/features/user/gui/desktops/niri/parts/ignis/default.nix @@ -0,0 +1,21 @@ +{ config, pkgs, lib, inputs, ... }: let + cfg = config.features.gui.desktops.niri.parts.ignis; +in { + options.features.gui.desktops.niri.parts.ignis.enable = lib.mkEnableOption "ignis"; + config = lib.mkIf cfg.enable { + home.packages = [ + inputs.ignis.packages.${pkgs.system}.ignis + pkgs.python3 + ]; + # home.file."ignis-config" = { + # target = ".config/ignis/config.py"; + # src = ./config.py; + # }; + # home.file."ignis-style" = { + # target = ".config/ignis/style.scss"; + # text = /*scss*/ '' + + # ''; + # }; + }; +} diff --git a/features/user/gui/desktops/niri/parts/mako.nix b/features/user/gui/desktops/niri/parts/mako.nix new file mode 100755 index 0000000..e5bc3b8 --- /dev/null +++ b/features/user/gui/desktops/niri/parts/mako.nix @@ -0,0 +1,26 @@ +{ + config, + lib, + ... +}: let + cfg = config.features.gui.desktops.niri.parts.mako; +in { + options.features.gui.desktops.niri.parts.mako.enable = lib.mkEnableOption "mako"; + config = lib.mkIf cfg.enable { + services.mako = { + enable = true; + settings = { + border-radius = 4; + border-size = 3; + # margin = "11"; + margin = "31"; + padding = "5"; + + anchor = "top-center"; + + ignore-timeout = true; + default-timeout = 10000; + }; + }; + }; +} diff --git a/features/user/gui/desktops/niri/parts/selectors.nix b/features/user/gui/desktops/niri/parts/selectors.nix new file mode 100644 index 0000000..f0f9ce0 --- /dev/null +++ b/features/user/gui/desktops/niri/parts/selectors.nix @@ -0,0 +1,117 @@ +{ + config, + pkgs, + lib, + ... +}: let + cfg = config.features.gui.desktops.niri.parts.selectors; + aes = config.aesthetics; +in { + options.features.gui.desktops.niri.parts.selectors.enable = lib.mkEnableOption "selectors"; + config = lib.mkIf cfg.enable { + home.packages = with pkgs; [ + # Tool Selector + ( + pkgs.writers.writeNuBin "tool-selector" + /* + nu + */ + '' + + # Tools + let tools = { + "rebuild nixos": { + footclient -H sudo nixos-rebuild switch --flake ($"~/Sync/setup#(hostname)" | path expand) + } + "rebuild home": { + footclient -H home-manager switch --flake ($"~/Sync/setup#(whoami)@(hostname)" | path expand) + } + "update flake": { + footclient -H nix flake update --flake ($"~/Sync/setup/" | path expand) + } + "manage wifi": { + footclient ${pkgs.impala}/bin/impala + } + "manage bluetooth": { + footclient ${lib.getExe pkgs.bluetui} + } + "create qr-code": { + let temp_file = mktemp + let qr_code_bin = ${lib.getExe pkgs.qrtool} encode (${pkgs.wl-clipboard}/bin/wl-paste) + $qr_code_bin | ${pkgs.wl-clipboard}/bin/wl-copy + $qr_code_bin | save -f $temp_file + ${lib.getExe pkgs.imv} $temp_file + } + } + + # Logic + let user_tool_choice = $tools + | columns + | to text + | fuzzel -d --placeholder "Tools" + if ($user_tool_choice != "") { + do ($tools | get $user_tool_choice) + } + + '' + ) + + # Wallpaper Selector + ( + writers.writeNuBin "wallpaper-selector" + /* + nu + */ + '' + def main [ + --all-outputs # Change wallpaper for all outputs + --randomize + ] { + mut wallpapers = {} + for path in (ls ${aes.wallpapersDir}/**/* | where {|item| $item.type != dir} | get name) { + $wallpapers = $wallpapers | insert ($path | path basename | split row "." | get 0) $path + } + mut prompt = "Wallpaper (current)" + if $all_outputs { + $prompt = "Wallpaper (all)" + } + mut wallpaper_path = "" + if $randomize { + $wallpaper_path = $wallpapers | get ( + $wallpapers + | columns + | shuffle + | get 0 + ) + } else { + $wallpaper_path = $wallpapers | get ( + $wallpapers + | columns + | to text + | ${lib.getExe pkgs.fuzzel} -d --placeholder $prompt + ) + } + if $all_outputs { + ${lib.getExe pkgs.swww} img $wallpaper_path -t wipe --transition-fps 60 --transition-angle 45 + } else { + let focused_display = niri msg -j focused-output + | from json + | get name + ${lib.getExe pkgs.swww} img $wallpaper_path -t wipe --transition-fps 60 --transition-angle 45 --outputs $focused_display + } + } + '' + ) + + # Clipboard Selector + ( + writers.writeNuBin "clipboard-selector" '' + ${lib.getExe pkgs.cliphist} list + | cut -f 2- + | ${lib.getExe pkgs.fuzzel} --dmenu + | ${pkgs.wl-clipboard}/bin/wl-copy + '' + ) + ]; + }; +} diff --git a/features/user/gui/desktops/niri/parts/swww.nix b/features/user/gui/desktops/niri/parts/swww.nix new file mode 100755 index 0000000..dca163b --- /dev/null +++ b/features/user/gui/desktops/niri/parts/swww.nix @@ -0,0 +1,8 @@ +{ config, pkgs, lib, ... }: let + cfg = config.features.gui.desktops.niri.parts.swww; +in { + options.features.gui.desktops.niri.parts.swww.enable = lib.mkEnableOption "swww"; + config = lib.mkIf cfg.enable { + home.packages = [pkgs.swww]; + }; +} diff --git a/features/user/gui/desktops/niri/parts/waybar.nix b/features/user/gui/desktops/niri/parts/waybar.nix new file mode 100755 index 0000000..7535b0c --- /dev/null +++ b/features/user/gui/desktops/niri/parts/waybar.nix @@ -0,0 +1,136 @@ +{ config, pkgs, lib, ... }: +let + cfg = config.features.gui.desktops.niri.parts.waybar; + aes = config.aesthetics; +in { + options.features.gui.desktops.niri.parts.waybar.enable = lib.mkEnableOption "waybar"; + config = lib.mkIf cfg.enable { + programs.waybar = { + enable = true; + settings = { + bar = { + layer = "top"; + position = "bottom"; + height = 32; + modules-left = [ "battery" "network" "backlight" "pulseaudio" ]; + modules-center = [ "niri/workspaces" ]; + modules-right = [ "clock#date" "clock#time" ]; + "clock#date" = { + format = " {:%A, %B %d}"; + }; + "clock#time" = { + format = " {:%I:%M}"; + }; + pulseaudio = { + format = " {volume}%"; + format-muted = " {volume}%"; + }; + network = { + format = "{essid}"; + format-wifi = "{icon} {essid}"; + format-ethernet = "󰈀 Ethernet"; + format-disconnected = "󰤭 Disconnected"; + format-icons = [ "󰤯" "󰤟" "󰤢" "󰤥" "󰤨" ]; + }; + battery = { + format = "{icon} {capacity}%"; + format-charging = "󰂄 {capacity}%"; + format-icons = [ "󰂎" "󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰂂" "󰁹" ]; + }; + backlight = { + format = " {percent}%"; + }; + "niri/workspaces" = { + format = "{icon}"; + format-icons = { + default = ""; + active = ""; + /* + "1" = "1"; + "2" = "2"; + "3" = "3"; + "4" = "4"; + "5" = "5"; + "6" = "6"; + "7" = "7"; + "8" = "8"; + "9" = "9"; + "10" = "10"; + "11" = "11"; + "12" = "12"; + */ + }; + }; + }; + }; + style = let + border-radius = "4"; + padding = "12"; + in /*css*/ '' + @define-color background-color #${aes.scheme.base00}; + @define-color border-color #${aes.scheme.base0C}; + @define-color text-color #${aes.scheme.base05}; + * { + font-family: ${aes.font.name}; + font-weight: 600; + font-size: ${aes.font.size.medium}px; + } + window#waybar { + background-color: transparent; + } + #clock.time, #clock.date, #backlight, #pulseaudio, #battery, #network { + background-color: @background-color; + color: @text-color; + border-radius: ${border-radius}px; + border-width: 0px; + border-color: @border-color; + padding: 0px ${padding}px; + } + #backlight, #pulseaudio, #battery, #network { + margin: 0px 0px ${padding} ${padding}; + } + #workspaces { + background-color: @background-color; + color: @background-color; + border-radius: ${border-radius}px; + border-width: 0px; + border-color: @border-color; + padding: 0px 0px; + margin-bottom: ${padding}px; + } + #workspaces button { + font-weight: bold; + padding: 0px 4px; + margin: 4px 4px; + border-radius: ${border-radius}px; + color: @background-color; + background: @text-color; + opacity: 0.5; + transition: all 0.3s cubic-bezier(.25,.1,.25,1); + } + #workspaces button.active { + font-weight: bold; + padding: 0px 4px; + margin: 4px 4px; + border-radius: ${border-radius}px; + color: @background-color; + background: @text-color; + transition: all 0.3s cubic-bezier(.25,.1,.25,1); + opacity: 1.0; + min-width: 40px; + } + #workspaces button:hover { + font-weight: bold; + border-radius: ${border-radius}px; + color: @background-color; + background: @text-color; + opacity: 0.8; + transition: all 0.3s cubic-bezier(.25,.1,.25,1); + } + #clock.date, #clock.time { + margin: 0px ${padding} ${padding} 0px + } + ''; + }; + }; +} diff --git a/features/user/gui/desktops/niri/parts/wl-kbptr.nix b/features/user/gui/desktops/niri/parts/wl-kbptr.nix new file mode 100644 index 0000000..6f6ed56 --- /dev/null +++ b/features/user/gui/desktops/niri/parts/wl-kbptr.nix @@ -0,0 +1,14 @@ +{ config, lib, pkgs, inputs, ... }: let + cfg = config.features.gui.desktops.niri.parts.wl-kbptr; +in { + options.features.gui.desktops.niri.parts.wl-kbptr.enable = lib.mkEnableOption "wl-kbptr"; + config = lib.mkIf cfg.enable { + home.packages = [ pkgs.wl-kbptr ]; + home.file."wl-kbptr-config" = { + target = ".config/wl-kbptr/config"; + text = '' + + ''; + }; + }; +} diff --git a/features/user/gui/desktops/niri/parts/wluma.nix b/features/user/gui/desktops/niri/parts/wluma.nix new file mode 100644 index 0000000..21b9edc --- /dev/null +++ b/features/user/gui/desktops/niri/parts/wluma.nix @@ -0,0 +1,11 @@ +{ config, lib, ... }: let + cfg = config.features.gui.desktops.niri.parts.wluma; +in { + options.features.gui.desktops.niri.parts.wluma.enable = lib.mkEnableOption "wluma"; + config = lib.mkIf cfg.enable { + services.wluma = { + enable = true; + systemd.enable = true; + }; + }; +} diff --git a/features/user/gui/desktops/niri/readme.md b/features/user/gui/desktops/niri/readme.md new file mode 100644 index 0000000..a70f77d --- /dev/null +++ b/features/user/gui/desktops/niri/readme.md @@ -0,0 +1,9 @@ +# Keybinds Scheme + +OS: The OS + Arrows combination is for moving between windows within a workspace. + +OS + Shift: The OS + Shift + Arrows combination is for moving between workspaces. + +OS + Alt: The OS + Alt + Arrows combination is for moving between monitors. + +The control key can be used with any of these combos to bring the current window along with you. -- cgit v1.2.3