diff --git a/modules/desktop/shells/dms/default.nix b/modules/desktop/shells/dms/default.nix index 9e220de..3cc9b70 100644 --- a/modules/desktop/shells/dms/default.nix +++ b/modules/desktop/shells/dms/default.nix @@ -6,20 +6,11 @@ default = true; description = "GPU temp in DMS bar."; }; - obsidianSnippetsDir = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = "Legacy single Obsidian snippets dir for matugen."; - }; - obsidianConfigDirs = lib.mkOption { + obsidianVaults = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; - description = "Vault `.obsidian/` paths for matugen."; - }; - obsidianSnippetsDirs = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ ]; - description = "Explicit `.obsidian/snippets` paths."; + example = [ "/mnt/zimaos/Obsidian/Home/.obsidian" ]; + description = "Vault `.obsidian/` dirs; matugen writes `/snippets/matugen.css`."; }; extraRightBarWidgets = lib.mkOption { type = lib.types.listOf lib.types.attrs; @@ -81,29 +72,7 @@ ]; config = lib.mkIf dmsEnabled { - dms.enable = true; - dms.enableGpuTemp = cfg.enableGpuTemp or true; - dms.obsidianSnippetsDir = cfg.obsidianSnippetsDir or null; - dms.obsidianConfigDirs = cfg.obsidianConfigDirs or [ ]; - dms.obsidianSnippetsDirs = cfg.obsidianSnippetsDirs or [ ]; - dms.enableWvkbdToggle = cfg.enableWvkbdToggle or false; - dms.enableRbwLockToggle = cfg.enableRbwLockToggle or false; - dms.extraRightBarWidgets = - (lib.optionals (cfg.enableWvkbdToggle or false) [ - { - id = "wvkbdToggle"; - enabled = true; - } - ]) - ++ (lib.optionals (cfg.enableRbwLockToggle or false) [ - { - id = "rbwLockToggle"; - enabled = true; - } - ]) - ++ (cfg.extraRightBarWidgets or [ ]); programs.nix-monitor.rebuildCommand = rebuildCommand; - dms.bundleThirdPartyPlugins = cfg.bundleThirdPartyPlugins or true; }; }; } diff --git a/modules/desktop/shells/dms/home-manager/default.nix b/modules/desktop/shells/dms/home-manager/default.nix index 9b166b2..90f73fc 100644 --- a/modules/desktop/shells/dms/home-manager/default.nix +++ b/modules/desktop/shells/dms/home-manager/default.nix @@ -7,11 +7,21 @@ ... }: let home = config.home.homeDirectory; - cfg = config.dms; - gpuTempEnabled = cfg.enableGpuTemp; + dmsEnabled = lib.attrByPath [ "chiasson" "desktop" "shell" ] null osConfig == "dms"; + dmsOs = lib.attrByPath [ "chiasson" "desktop" "shells" "dms" ] { } osConfig; + gpuTempEnabled = dmsOs.enableGpuTemp or true; hyprlandEnabled = lib.attrByPath [ "chiasson" "desktop" "hyprland" "enable" ] false osConfig; discordEnabled = lib.attrByPath [ "chiasson" "home" "apps" "discord" "enable" ] false config; ohMyPoshEnabled = config.programs.oh-my-posh.enable or false; + obsidianSnippetDirs = map (v: v + "/snippets") (dmsOs.obsidianVaults or [ ]); + seedBarWidgets = + lib.optionals (dmsOs.enableWvkbdToggle or false) [ + { id = "wvkbdToggle"; enabled = true; } + ] + ++ lib.optionals (dmsOs.enableRbwLockToggle or false) [ + { id = "rbwLockToggle"; enabled = true; } + ] + ++ (dmsOs.extraRightBarWidgets or [ ]); matugenOutputDirs = lib.optionals hyprlandEnabled [ "${home}/.config/hypr" ] ++ lib.optionals ohMyPoshEnabled [ "${home}/.config/oh-my-posh" ] @@ -23,29 +33,28 @@ ensureMatugenOutputDirsScript = pkgs.writeShellScript "dms-ensure-matugen-output-dirs" '' set -euo pipefail ${lib.concatStringsSep "\n" (map (dir: "mkdir -p ${lib.escapeShellArg dir}") matugenOutputDirs)} + ${lib.concatStringsSep "\n" (map (dir: '' + snippetDir=${lib.escapeShellArg dir} + parentDir="$(dirname "$snippetDir")" + if [ -d "$snippetDir" ]; then + : + elif [ ! -d "$parentDir" ] || [ ! -w "$parentDir" ]; then + echo "dms: skipping Obsidian snippet dir '$snippetDir' (parent missing or not writable)" >&2 + else + mkdir -p "$snippetDir" + fi + '') obsidianSnippetDirs)} ${lib.optionalString ohMyPoshEnabled '' dest="$HOME/.config/oh-my-posh/theme.omp.json" template="$HOME/.config/matugen/templates/ohmyposh-theme.omp.json" - # matugen writes output_path in place; HM symlinks into the store are read-only and go stale. if [ -L "$dest" ] || { [ -e "$dest" ] && ! [ -f "$dest" ]; }; then rm -f "$dest" fi if [ ! -f "$dest" ]; then - # Seed from our matugen template (same generation closure), not programs.oh-my-posh.package - # which can reference a GC'd store path after profile churn. cp -L "$template" "$dest" fi ''} ''; - obsidianSnippetsDir = cfg.obsidianSnippetsDir; # legacy single path - obsidianSnippetDirs = - let - snippetsFromConfigDirs = map (d: "${d}/snippets") cfg.obsidianConfigDirs; - explicitSnippetsDirs = - cfg.obsidianSnippetsDirs - ++ lib.optionals (obsidianSnippetsDir != null) [obsidianSnippetsDir]; - in - lib.unique (snippetsFromConfigDirs ++ explicitSnippetsDirs); # Clears Quickshell QML bytecode cache so bar plugins reload (stale *.qmlc keeps old UI). dmsRestartOnceScript = pkgs.writeShellScript "dms-rbw-plugin-restart-once" '' @@ -75,7 +84,7 @@ "settings.json" = jsonFormat.generate "dms-settings-seed.json" ( import ../_private/default-settings.nix { inherit lib gpuTempEnabled; - extraRightBarWidgets = cfg.extraRightBarWidgets; + extraRightBarWidgets = seedBarWidgets; } ); }; @@ -108,83 +117,13 @@ }; in { - options = { - dms.enable = lib.mkOption { - type = lib.types.bool; - default = false; - description = "DMS Home Manager config from this flake."; - }; - - dms.enableGpuTemp = lib.mkOption { - type = lib.types.bool; - default = true; - description = "GPU temp widget in the bar."; - }; - - # /.obsidian/snippets — unset skips Obsidian matugen output - dms.obsidianSnippetsDir = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - example = "/home/olivier/Documents/Notes/.obsidian/snippets"; - description = "Snippet dir for `matugen.css` (legacy; prefer `obsidianConfigDirs`)."; - }; - - # Each path is a vault’s `.obsidian/` — we write `/snippets/matugen.css`. - dms.obsidianConfigDirs = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = []; - example = [ - "/mnt/zimaos/Obsidian/Home/.obsidian" - ]; - description = "Vault `.obsidian` dirs for matugen CSS."; - }; - - # Or pass `.obsidian/snippets` paths yourself. - dms.obsidianSnippetsDirs = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = []; - example = [ - "/mnt/zimaos/Obsidian/Home/.obsidian/snippets" - ]; - description = "Explicit snippet directories for matugen CSS."; - }; - - # Prepended to the bar’s rightWidgets (shows first). - dms.extraRightBarWidgets = lib.mkOption { - type = lib.types.listOf lib.types.attrs; - default = []; - example = [ - { id = "wvkbdToggle"; enabled = true; } - { id = "rbwLockToggle"; enabled = true; } - ]; - description = "Extra `{ id, enabled, … }` widgets merged into the first-run settings seed."; - }; - - dms.enableWvkbdToggle = lib.mkEnableOption '' - Ship wvkbd-toggle plugin into `~/.config/DankMaterialShell/plugins/` (toggle from NixOS `desktop.shells.dms`). - ''; - - dms.enableRbwLockToggle = lib.mkEnableOption '' - Ship rbw-lock-toggle into `~/.config/DankMaterialShell/plugins/rbwLockToggle/` (directory name matches plugin id; Bitwarden vault lock/unlock in the bar). - ''; - - dms.bundleThirdPartyPlugins = lib.mkOption { - type = lib.types.bool; - default = true; - description = '' - Install bundled third-party DMS plugins (DankVault, Calculator, Home Assistant, …) - into `~/.config/DankMaterialShell/plugins/` from flake inputs. - ''; - }; - }; - imports = [ inputs.dms.homeModules.dank-material-shell inputs.nix-monitor.homeManagerModules.default ]; - config = lib.mkIf cfg.enable (lib.mkMerge [ - (lib.mkIf cfg.enableWvkbdToggle { + config = lib.mkIf dmsEnabled (lib.mkMerge [ + (lib.mkIf (dmsOs.enableWvkbdToggle or false) { xdg.configFile."DankMaterialShell/plugins/wvkbd-toggle/plugin.json".source = ../plugins/wvkbd-toggle/plugin.json; xdg.configFile."DankMaterialShell/plugins/wvkbd-toggle/WvkbdToggle.qml".source = @@ -192,7 +131,7 @@ in { xdg.configFile."DankMaterialShell/plugins/wvkbd-toggle/WvkbdToggleSettings.qml".source = ../plugins/wvkbd-toggle/WvkbdToggleSettings.qml; }) - (lib.mkIf cfg.enableRbwLockToggle { + (lib.mkIf (dmsOs.enableRbwLockToggle or false) { # Directory name matches plugin id (same idea as NixMonitor / ideapad wvkbdToggle). xdg.configFile."DankMaterialShell/plugins/rbwLockToggle/plugin.json" = { source = ../plugins/rbw-lock-toggle/plugin.json; @@ -207,7 +146,7 @@ in { onChange = "${dmsRestartOnceScript}"; }; }) - (lib.mkIf cfg.bundleThirdPartyPlugins { + (lib.mkIf (dmsOs.bundleThirdPartyPlugins or true) { xdg.configFile = lib.mapAttrs (id: src: shipThirdPartyPlugin id src) thirdPartyPlugins; }) @@ -332,445 +271,11 @@ in { ) ; - # Obsidian snippet dirs for matugen: mkdir here instead of tmpfiles (no chmod fights on weird mounts). - home.activation.dmsEnsureObsidianSnippetDirs = lib.hm.dag.entryAfter ["writeBoundary"] ( - let - snippetDirs = lib.unique obsidianSnippetDirs; - escapedSnippetDirs = map lib.escapeShellArg snippetDirs; - in - '' - for snippetDir in ${lib.concatStringsSep " " escapedSnippetDirs}; do - parentDir="$(dirname "$snippetDir")" - - if [ -d "$snippetDir" ]; then - continue - fi - - if [ ! -d "$parentDir" ] || [ ! -w "$parentDir" ]; then - echo "dms: skipping Obsidian snippet dir '$snippetDir' (parent missing or not writable)" >&2 - continue - fi - - mkdir -p "$snippetDir" - done - '' - ); - xdg.configFile."matugen/templates/dank-discord.css".source = ../templates/dank-discord.css; - - # matugen template → Hyprland colors file sourced at runtime. - # Your Hyprland module includes: `source = ~/.config/hypr/colors.conf` - # (see `modules/desktop/hyprland/home-manager/appearance.nix`). - xdg.configFile."matugen/templates/hyprland-colors.conf".text = '' - # Hyprland colors generated by DMS/matugen - # Keep `.default` so DMS can swap to `.dark` when "Always use Dark Theme" is enabled. - - general { - col.active_border = rgb({{colors.primary.default.hex_stripped}}) rgb({{colors.secondary.default.hex_stripped}}) rgb({{colors.tertiary.default.hex_stripped}}) - col.inactive_border = rgba({{colors.surface_variant.default.hex_stripped}}ee) - } - - decoration { - shadow { - color = rgba({{colors.shadow.default.hex_stripped}}cc) - } - } - - # Hyprbars colors - plugin { - hyprbars { - bar_color = rgba({{colors.surface.default.hex_stripped}}ff) - col.text = rgba({{colors.on_surface.default.hex_stripped}}ff) - } - } - ''; - - # matugen → Oh My Posh JSON; hex in the base theme swapped for matugen tokens. - xdg.configFile."matugen/templates/ohmyposh-theme.omp.json".text = '' - { - "$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json", - "blocks": [ - { - "alignment": "left", - "segments": [ - { - "background": "#3A456E", - "foreground": "#ffbebc", - "leading_diamond": "\ue0b6", - "style": "diamond", - "template": "\u007B\u007B .UserName \u007D\u007D@\u007B\u007B .HostName \u007D\u007D <#ffffff>on", - "type": "session" - }, - { - "type": "os", - "style": "diamond", - "background": "#3A456E", - "foreground": "#ffffff", - "trailing_diamond": "\ue0b4", - "template": " \u007B\u007B .Icon \u007D\u007D " - }, - {"type": "session", - "style": "diamond", - "background": "#3A456E", - "foreground": "#ffbebc", - "template": "\u007B\u007B if .SSHSession \u007D\u007Dvia SSH\u007B\u007B end \u007D\u007D", - "trailing_diamond": "\ue0b4", - "type": "session" - }, - { - "type": "text", - "style": "diamond", - "background": "#3A456E", - "foreground": "#ffbebc", - "template": "\u007B\u007B if .Env.IN_NIX_SHELL \u007D\u007Din nix-shell\u007B\u007B end \u007D\u007D", - "trailing_diamond": "\ue0b4" - }, - { - "background": "#3A456E", - "foreground": "#bc93ff", - "leading_diamond": "\ue0b6", - "properties": { - "time_format": "Monday <#ffffff>at 3:04 PM" - }, - "style": "diamond", - "trailing_diamond": "\ue0b4", - "template": "\u007B\u007B .CurrentDate | date .Format \u007D\u007D", - "type": "time" - }, - - - { - "background": "#3A456E", - "foreground": "#43CCEA", - "foreground_templates": [ - "\u007B\u007B if or (.Working.Changed) (.Staging.Changed) \u007D\u007D#FF9248\u007B\u007B end \u007D\u007D", - "\u007B\u007B if and (gt .Ahead 0) (gt .Behind 0) \u007D\u007D#ff4500\u007B\u007B end \u007D\u007D", - "\u007B\u007B if gt .Ahead 0 \u007D\u007D#B388FF\u007B\u007B end \u007D\u007D", - "\u007B\u007B if gt .Behind 0 \u007D\u007D#B388FF\u007B\u007B end \u007D\u007D" - ], - "leading_diamond": " \ue0b6", - "options": { - "branch_template": "\u007B\u007B trunc 25 .Branch \u007D\u007D", - "fetch_status": true, - "fetch_upstream_icon": true - }, - "style": "diamond", - "template": " \u007B\u007B .UpstreamIcon \u007D\u007D\u007B\u007B .HEAD \u007D\u007D\u007B\u007Bif .BranchStatus \u007D\u007D \u007B\u007B .BranchStatus \u007D\u007D\u007B\u007B end \u007D\u007D\u007B\u007B if .Working.Changed \u007D\u007D \uf044 \u007B\u007B .Working.String \u007D\u007D\u007B\u007B end \u007D\u007D\u007B\u007B if and (.Working.Changed) (.Staging.Changed) \u007D\u007D |\u007B\u007B end \u007D\u007D\u007B\u007B if .Staging.Changed \u007D\u007D \uf046 \u007B\u007B .Staging.String \u007D\u007D\u007B\u007B end \u007D\u007D\u007B\u007B if gt .StashCount 0 \u007D\u007D \ueb4b \u007B\u007B .StashCount \u007D\u007D\u007B\u007B end \u007D\u007D ", - "trailing_diamond": "\ue0b4", - "type": "git" - }, - { - "background": "#3A456E", - "foreground": "#E4F34A", - "leading_diamond": " \ue0b6", - "options": { - "fetch_version": false - }, - "style": "diamond", - "template": "\ue235 \u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B if .Venv \u007D\u007D\u007B\u007B .Venv \u007D\u007D \u007B\u007B end \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D", - "trailing_diamond": "\ue0b4", - "type": "python" - }, - { - "background": "#3A456E", - "foreground": "#7FD5EA", - "leading_diamond": " \ue0b6", - "options": { - "fetch_version": false - }, - "style": "diamond", - "template": "\ue626\u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D", - "trailing_diamond": "\ue0b4", - "type": "go" - }, - { - "background": "#3A456E", - "foreground": "#42E66C", - "leading_diamond": " \ue0b6", - "options": { - "fetch_version": false - }, - "style": "diamond", - "template": "\ue718\u007B\u007B if .PackageManagerIcon \u007D\u007D\u007B\u007B .PackageManagerIcon \u007D\u007D \u007B\u007B end \u007D\u007D\u007B\u007B .Full \u007D\u007D", - "trailing_diamond": "\ue0b4", - "type": "node" - }, - { - "background": "#3A456E", - "foreground": "#E64747", - "leading_diamond": " \ue0b6", - "options": { - "fetch_version": false - }, - "style": "diamond", - "template": "\ue791\u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D", - "trailing_diamond": "\ue0b4", - "type": "ruby" - }, - { - "background": "#3A456E", - "foreground": "#E64747", - "leading_diamond": " \ue0b6", - "options": { - "fetch_version": false - }, - "style": "diamond", - "template": "\ue738\u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D", - "trailing_diamond": "\ue0b4", - "type": "java" - }, - { - "background": "#3A456E", - "foreground": "#9B6BDF", - "leading_diamond": " \ue0b6", - "options": { - "fetch_version": false - }, - "style": "diamond", - "template": "\ue624\u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D ", - "trailing_diamond": "\ue0b4", - "type": "julia" - }, - { - "type": "php", - "style": "diamond", - "foreground": "#ffffff", - "background": "#4063D8", - "leading_diamond": " \ue0b6", - "options": { - "fetch_version": false - }, - "template": "\ue73d \u007B\u007B .Full \u007D\u007D ", - "trailing_diamond": "\ue0b4" - }, - { - "background": "#3A456E", - "foreground": "#9B6BDF", - "foreground_templates": [ - "\u007B\u007Bif eq \"Charging\" .State.String\u007D\u007D#40c4ff\u007B\u007Bend\u007D\u007D", - "\u007B\u007Bif eq \"Discharging\" .State.String\u007D\u007D#ff5722\u007B\u007Bend\u007D\u007D", - "\u007B\u007Bif eq \"Full\" .State.String\u007D\u007D#4caf50\u007B\u007Bend\u007D\u007D" - ], - "leading_diamond": " \ue0b6", - "options": { - "charged_icon": "• ", - "charging_icon": "\u21e1 ", - "discharging_icon": "\u21e3 " - }, - "style": "diamond", - "template": "\u007B\u007B if not .Error \u007D\u007D\u007B\u007B .Icon \u007D\u007D\u007B\u007B .Percentage \u007D\u007D\u007B\u007B end \u007D\u007D\u007B\u007B .Error \u007D\u007D", - "trailing_diamond": "\ue0b4", - "type": "battery" - } - ], - "type": "prompt" - }, - { - "alignment": "right", - "segments": [ - { - "background": "#3A456E", - "foreground": "#AEA4BF", - "leading_diamond": "\ue0b6", - "options": { - "style": "austin", - "threshold": 150 - }, - "style": "diamond", - "template": "\u007B\u007B .FormattedMs \u007D\u007D", - "trailing_diamond": "\ue0b4 ", - "type": "executiontime" - } - ], - "type": "prompt" - }, - { - "alignment": "left", - "newline": true, - "segments": [ - { - "background": "#3A456E", - "foreground": "#3EC669", - "leading_diamond": "\ue0b6", - "properties": { - "style": "agnoster_full", - "cycle": [ - "#3EC669,#3A456E", - "#43CCEA,#3A456E", - "#E4F34A,#3A456E", - "#9B6BDF,#3A456E" - ], - "cycle_folder_separator": true - }, - "style": "diamond", - "template": "\ue5ff \u007B\u007B .Path \u007D\u007D", - "trailing_diamond": "\ue0b4", - "type": "path" - }, - { - "background": "#3A456E", - "foreground": "#ffbebc", - "leading_diamond": "\ue0b6", - "style": "diamond", - "template": "\ue602", - "trailing_diamond": "\ue0b4", - "type": "text" - } - ], - "type": "prompt" - } - ], - "final_space": true, - "version": 4 - } - ''; - - # matugen template → Obsidian CSS snippet (Minimal-compatible variables). - # - # Upstream template: - # https://raw.githubusercontent.com/Simorg2002/obsidian-matugen-template/refs/heads/main/obsidian-minimal-matugen-colors.css - xdg.configFile."matugen/templates/obsidian-minimal-matugen-colors.css".text = '' - .theme-dark { - --accent-h:calc({{colors.primary.dark.hue}}/255*360); - --accent-s:calc({{colors.primary.dark.saturation}}/255*100%); - --accent-l:calc({{colors.primary.dark.lightness}}/255*100%); - - --text-normal:{{colors.on_surface.dark.hex}}; - --text-muted:{{colors.on_surface_variant.dark.hex}}; - --text-faint:{{colors.outline.dark.hex}}; - - --background-primary:{{colors.surface.dark.hex}}; /* editor and right ribbon */ - --background-secondary:{{colors.surface_dim.dark.hex}}; /* left ribbon */ - --background-modifier-hover:{{colors.secondary_container.dark.hex}}; - --background-modifier-active-hover:{{colors.secondary.dark.hex}}; - --background-modifier-message:{{colors.surface_dim.dark.hex}}; - --background-modifier-form-field:{{colors.primary_container.dark.hex}}; - - --ribbon-background:{{colors.surface.dark.hex}}; - --divider-color:{{colors.secondary_container.dark.hex}}; - --scrollbar-thumb-bg:{{colors.on_secondary_container.dark.hex}}; - --status-bar-border-color:{{colors.secondary_container.dark.hex}}; - --status-bar-background:{{colors.surface.dark.hex}}; - --titlebar-background:{{colors.surface.dark.hex}}; - --titlebar-text-color:{{colors.on_surface.dark.hex}}; - - --nav-item-color:{{colors.on_surface_variant.dark.hex}}; - --nav-item-color-active:{{colors.on_primary_container.dark.hex}}; - --nav-item-background-active:{{colors.primary_container.dark.hex}}; - --nav-item-color-hover:{{colors.on_secondary_container.dark.hex}}; - --nav-item-background-hover:{{colors.secondary_container.dark.hex}}; - - --tab-background-active:{{colors.primary_container.dark.hex}}; - --tab-text-color-active:{{colors.on_primary_container.dark.hex}}; - --tab-text-color:{{colors.on_surface_variant.dark.hex}}; - - --icon-color:{{colors.on_surface.dark.hex}}; - - --h1-color:{{colors.primary.dark.hex}}; - --h2-color: color-mix(in srgb, {{colors.primary.dark.hex}}, {{colors.tertiary.dark.hex}}); - --h3-color:{{colors.tertiary.dark.hex}}; - --h4-color: color-mix(in srgb, {{colors.primary.dark.hex}}, {{colors.secondary.dark.hex}}); - --h5-color:{{colors.secondary.dark.hex}}; - --h6-color: color-mix(in srgb, {{colors.tertiary.dark.hex}}, {{colors.secondary.dark.hex}}); - - - } - - - /* add parts not accessible without specific selector?*/ - .theme-dark .sidebar-toggle-button.mod-left { - color:{{colors.on_surface.dark.hex}}; - background:{{colors.surface.dark.hex}}; - } - .theme-dark .sidebar-toggle-button.mod-right { - color:{{colors.on_surface.dark.hex}}; - background:{{colors.surface.dark.hex}}; - } - - .theme-dark .workspace-tab-header-container{ - background:{{colors.surface.dark.hex}}; - } - - - /* copy for light mode */ - .theme-light { - --accent-h:calc({{colors.primary.light.hue}}/255*360); - --accent-s:calc({{colors.primary.light.saturation}}/255*100%); - --accent-l:calc({{colors.primary.light.lightness}}/255*100%); - - --text-normal:{{colors.on_surface.light.hex}}; - --text-muted:{{colors.on_surface_variant.light.hex}}; - --text-faint:{{colors.outline.light.hex}}; - - --background-primary:{{colors.surface.light.hex}}; /* editor and right ribbon */ - --background-secondary:{{colors.surface_dim.light.hex}}; /* left ribbon */ - --background-modifier-hover:{{colors.secondary_container.light.hex}}; - --background-modifier-active-hover:{{colors.secondary.light.hex}}; - --background-modifier-message:{{colors.surface_dim.light.hex}}; - --background-modifier-form-field:{{colors.primary_container.light.hex}}; - - --ribbon-background:{{colors.surface.light.hex}}; - --divider-color:{{colors.secondary_container.light.hex}}; - --scrollbar-thumb-bg:{{colors.on_secondary_container.light.hex}}; - --status-bar-border-color:{{colors.secondary_container.light.hex}}; - --status-bar-background:{{colors.surface.light.hex}}; - --titlebar-background:{{colors.surface.light.hex}}; - --titlebar-text-color:{{colors.on_surface.light.hex}}; - - --nav-item-color:{{colors.on_surface_variant.light.hex}}; - --nav-item-color-active:{{colors.on_primary_container.light.hex}}; - --nav-item-background-active:{{colors.primary_container.light.hex}}; - --nav-item-color-hover:{{colors.on_secondary_container.light.hex}}; - --nav-item-background-hover:{{colors.secondary_container.light.hex}}; - - --tab-background-active:{{colors.primary_container.light.hex}}; - --tab-text-color-active:{{colors.on_primary_container.light.hex}}; - --tab-text-color:{{colors.on_surface_variant.light.hex}}; - - --icon-color:{{colors.on_surface.light.hex}}; - - --h1-color:{{colors.primary.light.hex}}; - --h2-color: color-mix(in srgb, {{colors.primary.light.hex}}, {{colors.tertiary.light.hex}}); - --h3-color:{{colors.tertiary.light.hex}}; - --h4-color: color-mix(in srgb, {{colors.primary.light.hex}}, {{colors.secondary.light.hex}}); - --h5-color:{{colors.secondary.light.hex}}; - --h6-color: color-mix(in srgb, {{colors.tertiary.light.hex}}, {{colors.secondary.light.hex}}); - - - } - - - /* add parts not accessible without specific selector?*/ - .theme-light .sidebar-toggle-button.mod-left { - color:{{colors.on_surface.light.hex}}; - background:{{colors.surface.light.hex}}; - } - .theme-light .sidebar-toggle-button.mod-right { - color:{{colors.on_surface.light.hex}}; - background:{{colors.surface.light.hex}}; - } - - .theme-light .workspace-tab-header-container{ - background:{{colors.surface.light.hex}}; - } - - /* Hide window controls (frameless titlebar buttons) */ - div[aria-label="Close window"], - div[aria-label="Minimize"], - div[aria-label="Restore down"], - div[aria-label="Maximize"]{ - display: none !important; - } - - /* Remove the extra right-side spacing reserved for those buttons */ - .is-hidden-frameless:not(.is-fullscreen) .workspace-tabs.mod-top-right-space .workspace-tab-header-container{ - padding-right: 0px !important; - } - - .is-hidden-frameless:not(.is-fullscreen) .workspace-tabs.mod-top-right-space .workspace-tab-header-container:after{ - display: none !important; - } - ''; + xdg.configFile."matugen/templates/hyprland-colors.conf".source = ../templates/hyprland-colors.conf; + xdg.configFile."matugen/templates/ohmyposh-theme.omp.json".source = ../templates/ohmyposh-theme.omp.json; + xdg.configFile."matugen/templates/obsidian-minimal-matugen-colors.css".source = + ../templates/obsidian-minimal-matugen-colors.css; } (lib.mkIf ohMyPoshEnabled { # Same path as matugen `[templates.ohmyposh]`; seeded as a real file by `ensureMatugenOutputDirsScript`. diff --git a/modules/desktop/shells/dms/templates/hyprland-colors.conf b/modules/desktop/shells/dms/templates/hyprland-colors.conf new file mode 100644 index 0000000..290a3a3 --- /dev/null +++ b/modules/desktop/shells/dms/templates/hyprland-colors.conf @@ -0,0 +1,21 @@ + # Hyprland colors generated by DMS/matugen + # Keep `.default` so DMS can swap to `.dark` when "Always use Dark Theme" is enabled. + + general { + col.active_border = rgb({{colors.primary.default.hex_stripped}}) rgb({{colors.secondary.default.hex_stripped}}) rgb({{colors.tertiary.default.hex_stripped}}) + col.inactive_border = rgba({{colors.surface_variant.default.hex_stripped}}ee) + } + + decoration { + shadow { + color = rgba({{colors.shadow.default.hex_stripped}}cc) + } + } + + # Hyprbars colors + plugin { + hyprbars { + bar_color = rgba({{colors.surface.default.hex_stripped}}ff) + col.text = rgba({{colors.on_surface.default.hex_stripped}}ff) + } + } diff --git a/modules/desktop/shells/dms/templates/obsidian-minimal-matugen-colors.css b/modules/desktop/shells/dms/templates/obsidian-minimal-matugen-colors.css new file mode 100644 index 0000000..5c99cb8 --- /dev/null +++ b/modules/desktop/shells/dms/templates/obsidian-minimal-matugen-colors.css @@ -0,0 +1,140 @@ + .theme-dark { + --accent-h:calc({{colors.primary.dark.hue}}/255*360); + --accent-s:calc({{colors.primary.dark.saturation}}/255*100%); + --accent-l:calc({{colors.primary.dark.lightness}}/255*100%); + + --text-normal:{{colors.on_surface.dark.hex}}; + --text-muted:{{colors.on_surface_variant.dark.hex}}; + --text-faint:{{colors.outline.dark.hex}}; + + --background-primary:{{colors.surface.dark.hex}}; /* editor and right ribbon */ + --background-secondary:{{colors.surface_dim.dark.hex}}; /* left ribbon */ + --background-modifier-hover:{{colors.secondary_container.dark.hex}}; + --background-modifier-active-hover:{{colors.secondary.dark.hex}}; + --background-modifier-message:{{colors.surface_dim.dark.hex}}; + --background-modifier-form-field:{{colors.primary_container.dark.hex}}; + + --ribbon-background:{{colors.surface.dark.hex}}; + --divider-color:{{colors.secondary_container.dark.hex}}; + --scrollbar-thumb-bg:{{colors.on_secondary_container.dark.hex}}; + --status-bar-border-color:{{colors.secondary_container.dark.hex}}; + --status-bar-background:{{colors.surface.dark.hex}}; + --titlebar-background:{{colors.surface.dark.hex}}; + --titlebar-text-color:{{colors.on_surface.dark.hex}}; + + --nav-item-color:{{colors.on_surface_variant.dark.hex}}; + --nav-item-color-active:{{colors.on_primary_container.dark.hex}}; + --nav-item-background-active:{{colors.primary_container.dark.hex}}; + --nav-item-color-hover:{{colors.on_secondary_container.dark.hex}}; + --nav-item-background-hover:{{colors.secondary_container.dark.hex}}; + + --tab-background-active:{{colors.primary_container.dark.hex}}; + --tab-text-color-active:{{colors.on_primary_container.dark.hex}}; + --tab-text-color:{{colors.on_surface_variant.dark.hex}}; + + --icon-color:{{colors.on_surface.dark.hex}}; + + --h1-color:{{colors.primary.dark.hex}}; + --h2-color: color-mix(in srgb, {{colors.primary.dark.hex}}, {{colors.tertiary.dark.hex}}); + --h3-color:{{colors.tertiary.dark.hex}}; + --h4-color: color-mix(in srgb, {{colors.primary.dark.hex}}, {{colors.secondary.dark.hex}}); + --h5-color:{{colors.secondary.dark.hex}}; + --h6-color: color-mix(in srgb, {{colors.tertiary.dark.hex}}, {{colors.secondary.dark.hex}}); + + + } + + + /* add parts not accessible without specific selector?*/ + .theme-dark .sidebar-toggle-button.mod-left { + color:{{colors.on_surface.dark.hex}}; + background:{{colors.surface.dark.hex}}; + } + .theme-dark .sidebar-toggle-button.mod-right { + color:{{colors.on_surface.dark.hex}}; + background:{{colors.surface.dark.hex}}; + } + + .theme-dark .workspace-tab-header-container{ + background:{{colors.surface.dark.hex}}; + } + + + /* copy for light mode */ + .theme-light { + --accent-h:calc({{colors.primary.light.hue}}/255*360); + --accent-s:calc({{colors.primary.light.saturation}}/255*100%); + --accent-l:calc({{colors.primary.light.lightness}}/255*100%); + + --text-normal:{{colors.on_surface.light.hex}}; + --text-muted:{{colors.on_surface_variant.light.hex}}; + --text-faint:{{colors.outline.light.hex}}; + + --background-primary:{{colors.surface.light.hex}}; /* editor and right ribbon */ + --background-secondary:{{colors.surface_dim.light.hex}}; /* left ribbon */ + --background-modifier-hover:{{colors.secondary_container.light.hex}}; + --background-modifier-active-hover:{{colors.secondary.light.hex}}; + --background-modifier-message:{{colors.surface_dim.light.hex}}; + --background-modifier-form-field:{{colors.primary_container.light.hex}}; + + --ribbon-background:{{colors.surface.light.hex}}; + --divider-color:{{colors.secondary_container.light.hex}}; + --scrollbar-thumb-bg:{{colors.on_secondary_container.light.hex}}; + --status-bar-border-color:{{colors.secondary_container.light.hex}}; + --status-bar-background:{{colors.surface.light.hex}}; + --titlebar-background:{{colors.surface.light.hex}}; + --titlebar-text-color:{{colors.on_surface.light.hex}}; + + --nav-item-color:{{colors.on_surface_variant.light.hex}}; + --nav-item-color-active:{{colors.on_primary_container.light.hex}}; + --nav-item-background-active:{{colors.primary_container.light.hex}}; + --nav-item-color-hover:{{colors.on_secondary_container.light.hex}}; + --nav-item-background-hover:{{colors.secondary_container.light.hex}}; + + --tab-background-active:{{colors.primary_container.light.hex}}; + --tab-text-color-active:{{colors.on_primary_container.light.hex}}; + --tab-text-color:{{colors.on_surface_variant.light.hex}}; + + --icon-color:{{colors.on_surface.light.hex}}; + + --h1-color:{{colors.primary.light.hex}}; + --h2-color: color-mix(in srgb, {{colors.primary.light.hex}}, {{colors.tertiary.light.hex}}); + --h3-color:{{colors.tertiary.light.hex}}; + --h4-color: color-mix(in srgb, {{colors.primary.light.hex}}, {{colors.secondary.light.hex}}); + --h5-color:{{colors.secondary.light.hex}}; + --h6-color: color-mix(in srgb, {{colors.tertiary.light.hex}}, {{colors.secondary.light.hex}}); + + + } + + + /* add parts not accessible without specific selector?*/ + .theme-light .sidebar-toggle-button.mod-left { + color:{{colors.on_surface.light.hex}}; + background:{{colors.surface.light.hex}}; + } + .theme-light .sidebar-toggle-button.mod-right { + color:{{colors.on_surface.light.hex}}; + background:{{colors.surface.light.hex}}; + } + + .theme-light .workspace-tab-header-container{ + background:{{colors.surface.light.hex}}; + } + + /* Hide window controls (frameless titlebar buttons) */ + div[aria-label="Close window"], + div[aria-label="Minimize"], + div[aria-label="Restore down"], + div[aria-label="Maximize"]{ + display: none !important; + } + + /* Remove the extra right-side spacing reserved for those buttons */ + .is-hidden-frameless:not(.is-fullscreen) .workspace-tabs.mod-top-right-space .workspace-tab-header-container{ + padding-right: 0px !important; + } + + .is-hidden-frameless:not(.is-fullscreen) .workspace-tabs.mod-top-right-space .workspace-tab-header-container:after{ + display: none !important; + } diff --git a/modules/desktop/shells/dms/templates/ohmyposh-theme.omp.json b/modules/desktop/shells/dms/templates/ohmyposh-theme.omp.json new file mode 100644 index 0000000..28bb664 --- /dev/null +++ b/modules/desktop/shells/dms/templates/ohmyposh-theme.omp.json @@ -0,0 +1,236 @@ + { + "$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json", + "blocks": [ + { + "alignment": "left", + "segments": [ + { + "background": "#3A456E", + "foreground": "#ffbebc", + "leading_diamond": "\ue0b6", + "style": "diamond", + "template": "\u007B\u007B .UserName \u007D\u007D@\u007B\u007B .HostName \u007D\u007D <#ffffff>on", + "type": "session" + }, + { + "type": "os", + "style": "diamond", + "background": "#3A456E", + "foreground": "#ffffff", + "trailing_diamond": "\ue0b4", + "template": " \u007B\u007B .Icon \u007D\u007D " + }, + {"type": "session", + "style": "diamond", + "background": "#3A456E", + "foreground": "#ffbebc", + "template": "\u007B\u007B if .SSHSession \u007D\u007Dvia SSH\u007B\u007B end \u007D\u007D", + "trailing_diamond": "\ue0b4", + "type": "session" + }, + { + "type": "text", + "style": "diamond", + "background": "#3A456E", + "foreground": "#ffbebc", + "template": "\u007B\u007B if .Env.IN_NIX_SHELL \u007D\u007Din nix-shell\u007B\u007B end \u007D\u007D", + "trailing_diamond": "\ue0b4" + }, + { + "background": "#3A456E", + "foreground": "#bc93ff", + "leading_diamond": "\ue0b6", + "properties": { + "time_format": "Monday <#ffffff>at 3:04 PM" + }, + "style": "diamond", + "trailing_diamond": "\ue0b4", + "template": "\u007B\u007B .CurrentDate | date .Format \u007D\u007D", + "type": "time" + }, + + + { + "background": "#3A456E", + "foreground": "#43CCEA", + "foreground_templates": [ + "\u007B\u007B if or (.Working.Changed) (.Staging.Changed) \u007D\u007D#FF9248\u007B\u007B end \u007D\u007D", + "\u007B\u007B if and (gt .Ahead 0) (gt .Behind 0) \u007D\u007D#ff4500\u007B\u007B end \u007D\u007D", + "\u007B\u007B if gt .Ahead 0 \u007D\u007D#B388FF\u007B\u007B end \u007D\u007D", + "\u007B\u007B if gt .Behind 0 \u007D\u007D#B388FF\u007B\u007B end \u007D\u007D" + ], + "leading_diamond": " \ue0b6", + "options": { + "branch_template": "\u007B\u007B trunc 25 .Branch \u007D\u007D", + "fetch_status": true, + "fetch_upstream_icon": true + }, + "style": "diamond", + "template": " \u007B\u007B .UpstreamIcon \u007D\u007D\u007B\u007B .HEAD \u007D\u007D\u007B\u007Bif .BranchStatus \u007D\u007D \u007B\u007B .BranchStatus \u007D\u007D\u007B\u007B end \u007D\u007D\u007B\u007B if .Working.Changed \u007D\u007D \uf044 \u007B\u007B .Working.String \u007D\u007D\u007B\u007B end \u007D\u007D\u007B\u007B if and (.Working.Changed) (.Staging.Changed) \u007D\u007D |\u007B\u007B end \u007D\u007D\u007B\u007B if .Staging.Changed \u007D\u007D \uf046 \u007B\u007B .Staging.String \u007D\u007D\u007B\u007B end \u007D\u007D\u007B\u007B if gt .StashCount 0 \u007D\u007D \ueb4b \u007B\u007B .StashCount \u007D\u007D\u007B\u007B end \u007D\u007D ", + "trailing_diamond": "\ue0b4", + "type": "git" + }, + { + "background": "#3A456E", + "foreground": "#E4F34A", + "leading_diamond": " \ue0b6", + "options": { + "fetch_version": false + }, + "style": "diamond", + "template": "\ue235 \u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B if .Venv \u007D\u007D\u007B\u007B .Venv \u007D\u007D \u007B\u007B end \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D", + "trailing_diamond": "\ue0b4", + "type": "python" + }, + { + "background": "#3A456E", + "foreground": "#7FD5EA", + "leading_diamond": " \ue0b6", + "options": { + "fetch_version": false + }, + "style": "diamond", + "template": "\ue626\u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D", + "trailing_diamond": "\ue0b4", + "type": "go" + }, + { + "background": "#3A456E", + "foreground": "#42E66C", + "leading_diamond": " \ue0b6", + "options": { + "fetch_version": false + }, + "style": "diamond", + "template": "\ue718\u007B\u007B if .PackageManagerIcon \u007D\u007D\u007B\u007B .PackageManagerIcon \u007D\u007D \u007B\u007B end \u007D\u007D\u007B\u007B .Full \u007D\u007D", + "trailing_diamond": "\ue0b4", + "type": "node" + }, + { + "background": "#3A456E", + "foreground": "#E64747", + "leading_diamond": " \ue0b6", + "options": { + "fetch_version": false + }, + "style": "diamond", + "template": "\ue791\u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D", + "trailing_diamond": "\ue0b4", + "type": "ruby" + }, + { + "background": "#3A456E", + "foreground": "#E64747", + "leading_diamond": " \ue0b6", + "options": { + "fetch_version": false + }, + "style": "diamond", + "template": "\ue738\u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D", + "trailing_diamond": "\ue0b4", + "type": "java" + }, + { + "background": "#3A456E", + "foreground": "#9B6BDF", + "leading_diamond": " \ue0b6", + "options": { + "fetch_version": false + }, + "style": "diamond", + "template": "\ue624\u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D ", + "trailing_diamond": "\ue0b4", + "type": "julia" + }, + { + "type": "php", + "style": "diamond", + "foreground": "#ffffff", + "background": "#4063D8", + "leading_diamond": " \ue0b6", + "options": { + "fetch_version": false + }, + "template": "\ue73d \u007B\u007B .Full \u007D\u007D ", + "trailing_diamond": "\ue0b4" + }, + { + "background": "#3A456E", + "foreground": "#9B6BDF", + "foreground_templates": [ + "\u007B\u007Bif eq \"Charging\" .State.String\u007D\u007D#40c4ff\u007B\u007Bend\u007D\u007D", + "\u007B\u007Bif eq \"Discharging\" .State.String\u007D\u007D#ff5722\u007B\u007Bend\u007D\u007D", + "\u007B\u007Bif eq \"Full\" .State.String\u007D\u007D#4caf50\u007B\u007Bend\u007D\u007D" + ], + "leading_diamond": " \ue0b6", + "options": { + "charged_icon": "• ", + "charging_icon": "\u21e1 ", + "discharging_icon": "\u21e3 " + }, + "style": "diamond", + "template": "\u007B\u007B if not .Error \u007D\u007D\u007B\u007B .Icon \u007D\u007D\u007B\u007B .Percentage \u007D\u007D\u007B\u007B end \u007D\u007D\u007B\u007B .Error \u007D\u007D", + "trailing_diamond": "\ue0b4", + "type": "battery" + } + ], + "type": "prompt" + }, + { + "alignment": "right", + "segments": [ + { + "background": "#3A456E", + "foreground": "#AEA4BF", + "leading_diamond": "\ue0b6", + "options": { + "style": "austin", + "threshold": 150 + }, + "style": "diamond", + "template": "\u007B\u007B .FormattedMs \u007D\u007D", + "trailing_diamond": "\ue0b4 ", + "type": "executiontime" + } + ], + "type": "prompt" + }, + { + "alignment": "left", + "newline": true, + "segments": [ + { + "background": "#3A456E", + "foreground": "#3EC669", + "leading_diamond": "\ue0b6", + "properties": { + "style": "agnoster_full", + "cycle": [ + "#3EC669,#3A456E", + "#43CCEA,#3A456E", + "#E4F34A,#3A456E", + "#9B6BDF,#3A456E" + ], + "cycle_folder_separator": true + }, + "style": "diamond", + "template": "\ue5ff \u007B\u007B .Path \u007D\u007D", + "trailing_diamond": "\ue0b4", + "type": "path" + }, + { + "background": "#3A456E", + "foreground": "#ffbebc", + "leading_diamond": "\ue0b6", + "style": "diamond", + "template": "\ue602", + "trailing_diamond": "\ue0b4", + "type": "text" + } + ], + "type": "prompt" + } + ], + "final_space": true, + "version": 4 + } diff --git a/modules/wisdom/default.nix b/modules/wisdom/default.nix index 21a2bc1..fa1ecd3 100644 --- a/modules/wisdom/default.nix +++ b/modules/wisdom/default.nix @@ -1,15 +1,5 @@ # HM side of the flake; option tree is `chiasson.home.*` (docs/conventions.md). { self, inputs, ... }: { - imports = [ - ./apps/discord.nix - ./apps/localsend.nix - ./apps/pokeclicker - ./apps/spotify.nix - ./browsers/orion.nix - ./desktop/screenshot.nix - ./hardware/uconsole-gamepad.nix - ]; - # Root module: chiasson.home.enable + bash. Other `wisdom*` slices auto-wire via # `lib.wisdomCatalogExtraModules`; hosts flip `chiasson.home.*.enable` rather than re-importing. flake.homeManagerModules.wisdom = diff --git a/modules/wisdom/desktop/screenshot.nix b/modules/wisdom/desktop/screenshot.nix index 99e28fe..4541929 100644 --- a/modules/wisdom/desktop/screenshot.nix +++ b/modules/wisdom/desktop/screenshot.nix @@ -7,6 +7,8 @@ hyprlandHm = lib.attrByPath [ "wayland" "windowManager" "hyprland" ] { } config; hyprlandHmEnabled = hyprlandHm.enable or false; keyOk = cfg.swiftshareApiKeyFile != null && cfg.swiftshareApiKeyFile != ""; + uploadScript = builtins.readFile ./scripts/swiftshare-upload.sh; + screenshotScript = builtins.readFile ./scripts/swiftshare-screenshot.sh; in { options.chiasson.home.desktop.screenshot.enable = lib.mkEnableOption '' @@ -33,171 +35,23 @@ (lib.mkIf (root.enable && cfg.enable && keyOk) ( let apiKeyFile = cfg.swiftshareApiKeyFile; + swiftshareUpload = pkgs.writeShellScriptBin "swiftshare-upload" ( + builtins.replaceStrings [ "@API_KEY_FILE@" ] [ apiKeyFile ] uploadScript + ); + swiftshareScreenshot = pkgs.writeShellScriptBin "swiftshare-screenshot" screenshotScript; in lib.mkMerge [ { home.packages = with pkgs; [ - grim - slurp - swappy - wl-clipboard - libnotify - (writeShellScriptBin "swiftshare-upload" '' - #!${pkgs.bash}/bin/bash - set -euo pipefail - - COPY_URL=0 - if [ "$#" -ge 1 ] && [ "$1" = "--copy-url" ]; then - COPY_URL=1 - shift - fi - - APP_NAME="" - if [ "$#" -ge 2 ] && [ "$1" = "--app-name" ]; then - APP_NAME="$2" - shift 2 - fi - - API_KEY_FILE=${lib.escapeShellArg apiKeyFile} - if [ -r "$API_KEY_FILE" ]; then - SWIFTSHARE_API_KEY="$(tr -d '\n' < "$API_KEY_FILE")" - fi - - if [ -z "''${SWIFTSHARE_API_KEY:-}" ]; then - ${pkgs.libnotify}/bin/notify-send "SwiftShare upload" "SwiftShare API key missing (expected readable: $API_KEY_FILE)" - echo "Error: SwiftShare API key missing (expected readable: $API_KEY_FILE)" >&2 - exit 1 - fi - - IMAGE_FILE="" - RESPONSE_FILE="" - cleanup() { - if [ -n "$RESPONSE_FILE" ] && [ -f "$RESPONSE_FILE" ]; then - rm -f "$RESPONSE_FILE" - fi - } - trap cleanup EXIT - - if [ "$#" -ge 1 ] && [ "$1" != "-" ]; then - IMAGE_FILE="$1" - if [ "''${IMAGE_FILE#'/'}" = "''${IMAGE_FILE}" ]; then - IMAGE_FILE="$(${pkgs.coreutils}/bin/readlink -f "''${IMAGE_FILE}")" - fi - if [ ! -f "$IMAGE_FILE" ]; then - echo "Error: file not found: $IMAGE_FILE" >&2 - exit 1 - fi - else - APP_NAME="''${APP_NAME:-screenshot}" - APP_NAME="''${APP_NAME%% *}" - APP_NAME="''${APP_NAME//[^A-Za-z0-9]/}" - APP_NAME="''${APP_NAME,,}" - if [ -z "$APP_NAME" ]; then - APP_NAME="screenshot" - fi - - IMAGE_FILE="$(${pkgs.coreutils}/bin/mktemp --suffix=.png "''${TMPDIR:-/tmp}/''${APP_NAME}_XXXXXX")" - cat > "$IMAGE_FILE" - fi - - if [ ! -s "$IMAGE_FILE" ]; then - ${pkgs.libnotify}/bin/notify-send "SwiftShare" "Empty capture (maybe canceled) – not uploading" - echo "Empty image file, not uploading." >&2 - exit 0 - fi - - RESPONSE_FILE="$(mktemp)" - set +e - HTTP_STATUS="$(${pkgs.curl}/bin/curl -sS -o "''${RESPONSE_FILE}" -w '%{http_code}' \ - -X POST "https://swiftshare.cloud/api/upload/sharex" \ - -F "upload=@''${IMAGE_FILE}" \ - -F "apiKey=''${SWIFTSHARE_API_KEY}")" - CURL_EXIT=$? - set -e - - RESPONSE="$(cat "''${RESPONSE_FILE}")" - - if [ "''${CURL_EXIT}" -ne 0 ]; then - ${pkgs.libnotify}/bin/notify-send "SwiftShare upload failed" "Network or HTTP error (curl exit ''${CURL_EXIT})" - echo "SwiftShare upload failed (curl exit ''${CURL_EXIT})." >&2 - echo "Response body:" >&2 - echo "''${RESPONSE}" >&2 - exit 1 - fi - - if ! echo "''${HTTP_STATUS}" | grep -qE '^2[0-9][0-9]$'; then - ERROR_MSG="$(${pkgs.jq}/bin/jq -r '.error // empty' <<< "''${RESPONSE}")" - if [ -z "''${ERROR_MSG}" ] || [ "''${ERROR_MSG}" = "null" ]; then - ERROR_MSG="Failed to upload file" - fi - ${pkgs.libnotify}/bin/notify-send "SwiftShare upload failed (''${HTTP_STATUS})" "''${ERROR_MSG}" - echo "SwiftShare upload failed (HTTP ''${HTTP_STATUS}): ''${ERROR_MSG}" >&2 - exit 1 - fi - - URL="$(${pkgs.jq}/bin/jq -r '.url // empty' <<< "''${RESPONSE}")" - THUMBNAIL="$(${pkgs.jq}/bin/jq -r '.thumbnail // empty' <<< "''${RESPONSE}")" - - if [ -z "$URL" ] || [ "$URL" = "null" ]; then - ${pkgs.libnotify}/bin/notify-send "SwiftShare upload failed" "Could not parse URL from response" - echo "Upload failed. Raw response:" >&2 - echo "''${RESPONSE}" >&2 - exit 1 - fi - - echo "$URL" - if [ -n "$THUMBNAIL" ] && [ "$THUMBNAIL" != "null" ]; then - echo "$THUMBNAIL" - fi - - if [ "$COPY_URL" = "1" ]; then - ${pkgs.wl-clipboard}/bin/wl-copy <<< "$URL" - fi - - if [ -n "$IMAGE_FILE" ] && [ -f "$IMAGE_FILE" ]; then - ${pkgs.libnotify}/bin/notify-send \ - -a "SwiftShare" \ - -i "$IMAGE_FILE" \ - -h string:image-path:"$IMAGE_FILE" \ - "SwiftShare upload" "Uploaded image: $URL" - else - ${pkgs.libnotify}/bin/notify-send "SwiftShare upload" "Uploaded image: $URL" - fi - '') - - (writeShellScriptBin "swiftshare-screenshot" '' - #!${pkgs.bash}/bin/bash - set -euo pipefail - - LOG_DIR="$HOME/.local/state/swiftshare" - mkdir -p "$LOG_DIR" - LOG_FILE="$LOG_DIR/screenshot.log" - - APP_CLASS="$(${pkgs.hyprland}/bin/hyprctl activewindow -j 2>/dev/null | ${pkgs.jq}/bin/jq -r '.class // .initialClass // empty' 2>/dev/null || true)" - APP_CLASS="''${APP_CLASS%% *}" - APP_CLASS="''${APP_CLASS//[^A-Za-z0-9]/}" - APP_CLASS="''${APP_CLASS,,}" - if [ -z "$APP_CLASS" ]; then - APP_CLASS="screenshot" - fi - - GEOM="$(${pkgs.slurp}/bin/slurp)" - SLURP_EXIT=$? - - if [ "$SLURP_EXIT" -ne 0 ] || [ -z "$GEOM" ]; then - ${pkgs.libnotify}/bin/notify-send "SwiftShare" "Capture canceled" - { - echo "==== $(date) ==== capture canceled (slurp exit $SLURP_EXIT, geom='$GEOM')" - } >>"$LOG_FILE" 2>&1 - exit 0 - fi - - { - echo "==== $(date) ====" - echo "Geometry: $GEOM" - ${pkgs.grim}/bin/grim -g "$GEOM" - | ${pkgs.swappy}/bin/swappy -f - -o - | swiftshare-upload --copy-url --app-name "$APP_CLASS" - } >>"$LOG_FILE" 2>&1 - '') + grim + slurp + swappy + wl-clipboard + libnotify + curl + jq + swiftshareUpload + swiftshareScreenshot ]; } (lib.mkIf hyprlandHmEnabled { diff --git a/modules/wisdom/desktop/scripts/swiftshare-screenshot.sh b/modules/wisdom/desktop/scripts/swiftshare-screenshot.sh new file mode 100644 index 0000000..e2a9620 --- /dev/null +++ b/modules/wisdom/desktop/scripts/swiftshare-screenshot.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -euo pipefail + +LOG_DIR="$HOME/.local/state/swiftshare" +mkdir -p "$LOG_DIR" +LOG_FILE="$LOG_DIR/screenshot.log" + +APP_CLASS="$(hyprctl activewindow -j 2>/dev/null | jq -r '.class // .initialClass // empty' 2>/dev/null || true)" +APP_CLASS="${APP_CLASS%% *}" +APP_CLASS="${APP_CLASS//[^A-Za-z0-9]/}" +APP_CLASS="${APP_CLASS,,}" +if [ -z "$APP_CLASS" ]; then + APP_CLASS="screenshot" +fi + +GEOM="$(slurp)" +SLURP_EXIT=$? + +if [ "$SLURP_EXIT" -ne 0 ] || [ -z "$GEOM" ]; then + notify-send "SwiftShare" "Capture canceled" + { + echo "==== $(date) ==== capture canceled (slurp exit $SLURP_EXIT, geom='$GEOM')" + } >>"$LOG_FILE" 2>&1 + exit 0 +fi + +{ + echo "==== $(date) ====" + echo "Geometry: $GEOM" + grim -g "$GEOM" - | swappy -f - -o - | swiftshare-upload --copy-url --app-name "$APP_CLASS" +} >>"$LOG_FILE" 2>&1 diff --git a/modules/wisdom/desktop/scripts/swiftshare-upload.sh b/modules/wisdom/desktop/scripts/swiftshare-upload.sh new file mode 100644 index 0000000..81af549 --- /dev/null +++ b/modules/wisdom/desktop/scripts/swiftshare-upload.sh @@ -0,0 +1,121 @@ +#!/usr/bin/env bash +# API key path substituted at build time (@API_KEY_FILE@). +set -euo pipefail + +COPY_URL=0 +if [ "$#" -ge 1 ] && [ "$1" = "--copy-url" ]; then + COPY_URL=1 + shift +fi + +APP_NAME="" +if [ "$#" -ge 2 ] && [ "$1" = "--app-name" ]; then + APP_NAME="$2" + shift 2 +fi + +API_KEY_FILE=@API_KEY_FILE@ +if [ -r "$API_KEY_FILE" ]; then + SWIFTSHARE_API_KEY="$(tr -d '\n' < "$API_KEY_FILE")" +fi + +if [ -z "${SWIFTSHARE_API_KEY:-}" ]; then + notify-send "SwiftShare upload" "SwiftShare API key missing (expected readable: $API_KEY_FILE)" + echo "Error: SwiftShare API key missing (expected readable: $API_KEY_FILE)" >&2 + exit 1 +fi + +IMAGE_FILE="" +RESPONSE_FILE="" +cleanup() { + if [ -n "$RESPONSE_FILE" ] && [ -f "$RESPONSE_FILE" ]; then + rm -f "$RESPONSE_FILE" + fi +} +trap cleanup EXIT + +if [ "$#" -ge 1 ] && [ "$1" != "-" ]; then + IMAGE_FILE="$1" + if [ "${IMAGE_FILE#/}" = "${IMAGE_FILE}" ]; then + IMAGE_FILE="$(readlink -f "${IMAGE_FILE}")" + fi + if [ ! -f "$IMAGE_FILE" ]; then + echo "Error: file not found: $IMAGE_FILE" >&2 + exit 1 + fi +else + APP_NAME="${APP_NAME:-screenshot}" + APP_NAME="${APP_NAME%% *}" + APP_NAME="${APP_NAME//[^A-Za-z0-9]/}" + APP_NAME="${APP_NAME,,}" + if [ -z "$APP_NAME" ]; then + APP_NAME="screenshot" + fi + + IMAGE_FILE="$(mktemp --suffix=.png "${TMPDIR:-/tmp}/${APP_NAME}_XXXXXX")" + cat > "$IMAGE_FILE" +fi + +if [ ! -s "$IMAGE_FILE" ]; then + notify-send "SwiftShare" "Empty capture (maybe canceled) – not uploading" + echo "Empty image file, not uploading." >&2 + exit 0 +fi + +RESPONSE_FILE="$(mktemp)" +set +e +HTTP_STATUS="$(curl -sS -o "${RESPONSE_FILE}" -w '%{http_code}' \ + -X POST "https://swiftshare.cloud/api/upload/sharex" \ + -F "upload=${IMAGE_FILE}" \ + -F "apiKey=${SWIFTSHARE_API_KEY}")" +CURL_EXIT=$? +set -e + +RESPONSE="$(cat "${RESPONSE_FILE}")" + +if [ "${CURL_EXIT}" -ne 0 ]; then + notify-send "SwiftShare upload failed" "Network or HTTP error (curl exit ${CURL_EXIT})" + echo "SwiftShare upload failed (curl exit ${CURL_EXIT})." >&2 + echo "Response body:" >&2 + echo "${RESPONSE}" >&2 + exit 1 +fi + +if ! echo "${HTTP_STATUS}" | grep -qE '^2[0-9][0-9]$'; then + ERROR_MSG="$(jq -r '.error // empty' <<< "${RESPONSE}")" + if [ -z "${ERROR_MSG}" ] || [ "${ERROR_MSG}" = "null" ]; then + ERROR_MSG="Failed to upload file" + fi + notify-send "SwiftShare upload failed (${HTTP_STATUS})" "${ERROR_MSG}" + echo "SwiftShare upload failed (HTTP ${HTTP_STATUS}): ${ERROR_MSG}" >&2 + exit 1 +fi + +URL="$(jq -r '.url // empty' <<< "${RESPONSE}")" +THUMBNAIL="$(jq -r '.thumbnail // empty' <<< "${RESPONSE}")" + +if [ -z "$URL" ] || [ "$URL" = "null" ]; then + notify-send "SwiftShare upload failed" "Could not parse URL from response" + echo "Upload failed. Raw response:" >&2 + echo "${RESPONSE}" >&2 + exit 1 +fi + +echo "$URL" +if [ -n "$THUMBNAIL" ] && [ "$THUMBNAIL" != "null" ]; then + echo "$THUMBNAIL" +fi + +if [ "$COPY_URL" = "1" ]; then + wl-copy <<< "$URL" +fi + +if [ -n "$IMAGE_FILE" ] && [ -f "$IMAGE_FILE" ]; then + notify-send \ + -a "SwiftShare" \ + -i "$IMAGE_FILE" \ + -h string:image-path:"$IMAGE_FILE" \ + "SwiftShare upload" "Uploaded image: $URL" +else + notify-send "SwiftShare upload" "Uploaded image: $URL" +fi