refactor: implement component-based architecture for enhanced maintainability

- Reorganize directory structure into core/, features/, and themes/
- Colocate application Nix logic, configs, scripts, and theme overrides
- Implement 'Inversion of Control' for theming: apps now pull theme-specific layouts
- Update flake.nix and shared library paths to match the new structure
- Document the new Feature-Centric architecture in README.md
This commit is contained in:
Bernardo Magri
2026-04-12 14:51:15 +01:00
parent a9ee79a5ce
commit bbdf34ced8
535 changed files with 119 additions and 127 deletions

View File

@@ -0,0 +1,25 @@
general.import = [ "~/.config/nomarchy/current/theme/alacritty.toml" ]
[env]
TERM = "xterm-256color"
[terminal]
osc52 = "CopyPaste"
[font]
normal = { family = "JetBrainsMono Nerd Font", style = "Regular" }
bold = { family = "JetBrainsMono Nerd Font", style = "Bold" }
italic = { family = "JetBrainsMono Nerd Font", style = "Italic" }
size = 9
[window]
padding.x = 14
padding.y = 14
decorations = "None"
[keyboard]
bindings = [
{ key = "Insert", mods = "Shift", action = "Paste" },
{ key = "Insert", mods = "Control", action = "Copy" },
{ key = "Return", mods = "Shift", chars = "\u001B\r" }
]

View File

@@ -0,0 +1,26 @@
{ config, pkgs, lib, ... }:
{
programs.alacritty = {
enable = lib.mkDefault true;
settings = lib.mkDefault {
env = {
TERM = "xterm-256color";
};
terminal = {
osc52 = "CopyPaste";
};
window = {
padding = { x = 14; y = 14; };
decorations = "None";
};
keyboard = {
bindings = [
{ key = "Insert"; mods = "Shift"; action = "Paste"; }
{ key = "Insert"; mods = "Control"; action = "Copy"; }
{ key = "Return"; mods = "Shift"; chars = "\\u001B\\r"; }
];
};
};
};
}

30
features/apps/browser.nix Normal file
View File

@@ -0,0 +1,30 @@
{ config, pkgs, lib, ... }:
let
nomarchyLib = import ../lib { inherit lib; };
activeThemeName = config.nomarchy.system.theme;
currentPalette = nomarchyLib.getPalette activeThemeName;
# Hex color for browser theme (base00 is background)
themeColor = "#${currentPalette.base00}";
# Detect light mode from theme name or palette
isLightTheme = nomarchyLib.isThemeLightMode {
themeName = activeThemeName;
assetsPath = ../../assets/themes;
};
browserPolicy = {
BrowserThemeColor = themeColor;
BrowserColorScheme = if isLightTheme then "light" else "dark";
};
in
{
# Chromium policies
programs.chromium.extraOpts = lib.mkDefault browserPolicy;
# Brave browser policies via managed policy file
environment.etc."brave/policies/managed/nomarchy.json".text = lib.mkDefault (
builtins.toJSON browserPolicy
);
}

View File

@@ -0,0 +1,246 @@
#? Config file for btop
#* Name of a btop++/bpytop/bashtop formatted ".theme" file, "Default" and "TTY" for builtin themes.
#* Themes should be placed in "../share/btop/themes" relative to binary or "$HOME/.config/btop/themes"
color_theme = "current"
#* If the theme set background should be shown, set to False if you want terminal background transparency.
theme_background = True
#* Sets if 24-bit truecolor should be used, will convert 24-bit colors to 256 color (6x6x6 color cube) if false.
truecolor = True
#* Set to true to force tty mode regardless if a real tty has been detected or not.
#* Will force 16-color mode and TTY theme, set all graph symbols to "tty" and swap out other non tty friendly symbols.
force_tty = False
#* Define presets for the layout of the boxes. Preset 0 is always all boxes shown with default settings. Max 9 presets.
#* Format: "box_name:P:G,box_name:P:G" P=(0 or 1) for alternate positions, G=graph symbol to use for box.
#* Use whitespace " " as separator between different presets.
#* Example: "cpu:0:default,mem:0:tty,proc:1:default cpu:0:braille,proc:0:tty"
presets = "cpu:1:default,proc:0:default cpu:0:default,mem:0:default,net:0:default cpu:0:block,net:0:tty"
#* Set to True to enable "h,j,k,l,g,G" keys for directional control in lists.
#* Conflicting keys for h:"help" and k:"kill" is accessible while holding shift.
vim_keys = True
#* Rounded corners on boxes, is ignored if TTY mode is ON.
rounded_corners = True
#* Default symbols to use for graph creation, "braille", "block" or "tty".
#* "braille" offers the highest resolution but might not be included in all fonts.
#* "block" has half the resolution of braille but uses more common characters.
#* "tty" uses only 3 different symbols but will work with most fonts and should work in a real TTY.
#* Note that "tty" only has half the horizontal resolution of the other two, so will show a shorter historical view.
graph_symbol = "braille"
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
graph_symbol_cpu = "default"
# Graph symbol to use for graphs in gpu box, "default", "braille", "block" or "tty".
graph_symbol_gpu = "default"
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
graph_symbol_mem = "default"
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
graph_symbol_net = "default"
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
graph_symbol_proc = "default"
#* Manually set which boxes to show. Available values are "cpu mem net proc" and "gpu0" through "gpu5", separate values with whitespace.
shown_boxes = "cpu mem net proc"
#* Update time in milliseconds, recommended 2000 ms or above for better sample times for graphs.
update_ms = 2000
#* Processes sorting, "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu direct",
#* "cpu lazy" sorts top process over time (easier to follow), "cpu direct" updates top process directly.
proc_sorting = "cpu lazy"
#* Reverse sorting order, True or False.
proc_reversed = False
#* Show processes as a tree.
proc_tree = False
#* Use the cpu graph colors in the process list.
proc_colors = True
#* Use a darkening gradient in the process list.
proc_gradient = True
#* If process cpu usage should be of the core it's running on or usage of the total available cpu power.
proc_per_core = False
#* Show process memory as bytes instead of percent.
proc_mem_bytes = True
#* Show cpu graph for each process.
proc_cpu_graphs = True
#* Use /proc/[pid]/smaps for memory information in the process info box (very slow but more accurate)
proc_info_smaps = False
#* Show proc box on left side of screen instead of right.
proc_left = False
#* (Linux) Filter processes tied to the Linux kernel(similar behavior to htop).
proc_filter_kernel = False
#* In tree-view, always accumulate child process resources in the parent process.
proc_aggregate = False
#* Sets the CPU stat shown in upper half of the CPU graph, "total" is always available.
#* Select from a list of detected attributes from the options menu.
cpu_graph_upper = "Auto"
#* Sets the CPU stat shown in lower half of the CPU graph, "total" is always available.
#* Select from a list of detected attributes from the options menu.
cpu_graph_lower = "Auto"
#* If gpu info should be shown in the cpu box. Available values = "Auto", "On" and "Off".
show_gpu_info = "Auto"
#* Toggles if the lower CPU graph should be inverted.
cpu_invert_lower = True
#* Set to True to completely disable the lower CPU graph.
cpu_single_graph = False
#* Show cpu box at bottom of screen instead of top.
cpu_bottom = False
#* Shows the system uptime in the CPU box.
show_uptime = True
#* Show cpu temperature.
check_temp = True
#* Which sensor to use for cpu temperature, use options menu to select from list of available sensors.
cpu_sensor = "Auto"
#* Show temperatures for cpu cores also if check_temp is True and sensors has been found.
show_coretemp = True
#* Set a custom mapping between core and coretemp, can be needed on certain cpus to get correct temperature for correct core.
#* Use lm-sensors or similar to see which cores are reporting temperatures on your machine.
#* Format "x:y" x=core with wrong temp, y=core with correct temp, use space as separator between multiple entries.
#* Example: "4:0 5:1 6:3"
cpu_core_map = ""
#* Which temperature scale to use, available values: "celsius", "fahrenheit", "kelvin" and "rankine".
temp_scale = "celsius"
#* Use base 10 for bits/bytes sizes, KB = 1000 instead of KiB = 1024.
base_10_sizes = False
#* Show CPU frequency.
show_cpu_freq = True
#* Draw a clock at top of screen, formatting according to strftime, empty string to disable.
#* Special formatting: /host = hostname | /user = username | /uptime = system uptime
clock_format = "%X"
#* Update main ui in background when menus are showing, set this to false if the menus is flickering too much for comfort.
background_update = True
#* Custom cpu model name, empty string to disable.
custom_cpu_name = ""
#* Optional filter for shown disks, should be full path of a mountpoint, separate multiple values with whitespace " ".
#* Begin line with "exclude=" to change to exclude filter, otherwise defaults to "most include" filter. Example: disks_filter="exclude=/boot /home/user".
disks_filter = ""
#* Show graphs instead of meters for memory values.
mem_graphs = True
#* Show mem box below net box instead of above.
mem_below_net = False
#* Count ZFS ARC in cached and available memory.
zfs_arc_cached = True
#* If swap memory should be shown in memory box.
show_swap = True
#* Show swap as a disk, ignores show_swap value above, inserts itself after first disk.
swap_disk = True
#* If mem box should be split to also show disks info.
show_disks = True
#* Filter out non physical disks. Set this to False to include network disks, RAM disks and similar.
only_physical = True
#* Read disks list from /etc/fstab. This also disables only_physical.
use_fstab = True
#* Setting this to True will hide all datasets, and only show ZFS pools. (IO stats will be calculated per-pool)
zfs_hide_datasets = False
#* Set to true to show available disk space for privileged users.
disk_free_priv = False
#* Toggles if io activity % (disk busy time) should be shown in regular disk usage view.
show_io_stat = True
#* Toggles io mode for disks, showing big graphs for disk read/write speeds.
io_mode = False
#* Set to True to show combined read/write io graphs in io mode.
io_graph_combined = False
#* Set the top speed for the io graphs in MiB/s (100 by default), use format "mountpoint:speed" separate disks with whitespace " ".
#* Example: "/mnt/media:100 /:20 /boot:1".
io_graph_speeds = ""
#* Set fixed values for network graphs in Mebibits. Is only used if net_auto is also set to False.
net_download = 100
net_upload = 100
#* Use network graphs auto rescaling mode, ignores any values set above and rescales down to 10 Kibibytes at the lowest.
net_auto = True
#* Sync the auto scaling for download and upload to whichever currently has the highest scale.
net_sync = True
#* Starts with the Network Interface specified here.
net_iface = ""
#* Show battery stats in top right if battery is present.
show_battery = True
#* Which battery to use if multiple are present. "Auto" for auto detection.
selected_battery = "Auto"
#* Set loglevel for "~/.config/btop/btop.log" levels are: "ERROR" "WARNING" "INFO" "DEBUG".
#* The level set includes all lower levels, i.e. "DEBUG" will show all logging info.
log_level = "WARNING"
#* Measure PCIe throughput on NVIDIA cards, may impact performance on certain cards.
nvml_measure_pcie_speeds = True
#* Horizontally mirror the GPU graph.
gpu_mirror_graph = True
#* Custom gpu0 model name, empty string to disable.
custom_gpu_name0 = ""
#* Custom gpu1 model name, empty string to disable.
custom_gpu_name1 = ""
#* Custom gpu2 model name, empty string to disable.
custom_gpu_name2 = ""
#* Custom gpu3 model name, empty string to disable.
custom_gpu_name3 = ""
#* Custom gpu4 model name, empty string to disable.
custom_gpu_name4 = ""
#* Custom gpu5 model name, empty string to disable.
custom_gpu_name5 = ""

View File

@@ -0,0 +1,15 @@
{
"extensions": {
"theme": {
"id": "",
"use_system": false,
"use_custom": false
}
},
"browser": {
"theme": {
"color_scheme": 2,
"user_color": 2
}
}
}

View File

@@ -0,0 +1 @@
async = false

View File

@@ -0,0 +1,3 @@
show_actions = false
only_search_title = true
history = false

View File

@@ -0,0 +1 @@
command = 'wl-copy && hyprctl dispatch sendshortcut "SHIFT, Insert,"'

View File

@@ -0,0 +1,37 @@
# Dynamic theme colors
config-file = ?"~/.config/nomarchy/current/theme/ghostty.conf"
# Font
font-family = "JetBrainsMono Nerd Font"
font-style = Regular
font-size = 9
# Window
window-theme = ghostty
window-padding-x = 14
window-padding-y = 14
confirm-close-surface=false
resize-overlay = never
gtk-toolbar-style = flat
# Cursor styling
cursor-style = "block"
cursor-style-blink = false
# Cursor styling + SSH session terminfo
# (all shell integration options must be passed together)
shell-integration-features = no-cursor,ssh-env
# Keyboard bindings
keybind = shift+insert=paste_from_clipboard
keybind = control+insert=copy_to_clipboard
keybind = super+control+shift+alt+arrow_down=resize_split:down,100
keybind = super+control+shift+alt+arrow_up=resize_split:up,100
keybind = super+control+shift+alt+arrow_left=resize_split:left,100
keybind = super+control+shift+alt+arrow_right=resize_split:right,100
# Slowdown mouse scrolling
mouse-scroll-multiplier = 0.95
# Fix general slowness on hyprland (https://github.com/ghostty-org/ghostty/discussions/3224)
async-backend = epoll

View File

@@ -0,0 +1,30 @@
include ~/.config/nomarchy/current/theme/kitty.conf
# Font
font_family JetBrainsMono Nerd Font
bold_italic_font auto
font_size 9.0
# Window
window_padding_width 14
hide_window_decorations yes
confirm_os_window_close 0
# Keybindings
map ctrl+insert copy_to_clipboard
map shift+insert paste_from_clipboard
# Allow remote access
allow_remote_control yes
# Aesthetics
cursor_shape block
cursor_blink_interval 0
shell_integration no-cursor
enable_audio_bell no
# Minimal Tab bar styling
tab_bar_edge bottom
tab_bar_style powerline
tab_powerline_style slanted
tab_title_template {title}{' :{}:'.format(num_windows) if num_windows > 1 else ''}

View File

25
features/apps/makima.nix Normal file
View File

@@ -0,0 +1,25 @@
{ config, pkgs, lib, ... }:
let
cfg = config.nomarchy.system.features.makima;
in
{
config = lib.mkIf cfg {
# If the user has makima-bin available in their overlays (as they originally used a custom package),
# this will install it. Otherwise, it will fail evaluation if not available in nixpkgs.
environment.systemPackages = [ pkgs.makima-bin ];
environment.etc."makima/AT Translated Set 2 keyboard.toml".source = ../../assets/makima + "/AT Translated Set 2 keyboard.toml";
systemd.services.makima = {
description = "Makima key remapping service";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
User = config.services.displayManager.autoLogin.user;
Environment = "MAKIMA_CONFIG=/etc/makima";
ExecStart = "${pkgs.makima-bin}/bin/makima";
Restart = "on-failure";
};
};
};
}

View File

@@ -0,0 +1,7 @@
# Run nomarchy-restart-makima after any changes
[remap]
KEY_LEFTMETA-KEY_LEFTSHIFT-KEY_F23 = ["KEY_LEFTMETA", "KEY_LEFTALT", "KEY_SPACE"]
[settings]
GRAB_DEVICE = "true"

View File

@@ -0,0 +1,5 @@
{
"$schema": "https://opencode.ai/config.json",
"theme": "system",
"autoupdate": false
}

View File

@@ -0,0 +1,5 @@
#!/bin/bash
# Launch the fastfetch TUI that gives information about the current system.
exec nomarchy-launch-or-focus-tui "bash -c 'fastfetch; read -n 1 -s'"

View File

@@ -0,0 +1,5 @@
#!/bin/bash
# Launch the Nomarchy audio controls TUI (provided by wiremix).
nomarchy-launch-or-focus-tui wiremix

View File

@@ -0,0 +1,7 @@
#!/bin/bash
# Launch the Nomarchy bluetooth controls TUI (provided by bluetui).
# Also attempts to unblock bluetooth service if rfkill had blocked it.
rfkill unblock bluetooth
exec nomarchy-launch-or-focus-tui bluetui

View File

@@ -0,0 +1,17 @@
#!/bin/bash
# Launch the default browser as determined by xdg-settings.
# Automatically converts --private into the correct flag for the given browser.
default_browser=$(xdg-settings get default-web-browser)
browser_exec=$(sed -n 's/^Exec=\([^ ]*\).*/\1/p' {~/.local,~/.nix-profile,/usr}/share/applications/$default_browser 2>/dev/null | head -1)
if $browser_exec --help | grep -q MOZ_LOG; then
private_flag="--private-window"
elif [[ $browser_exec =~ edge ]]; then
private_flag="--inprivate"
else
private_flag="--incognito"
fi
exec setsid uwsm-app -- "$browser_exec" "${@/--private/$private_flag}"

View File

@@ -0,0 +1,15 @@
#!/bin/bash
# Launch the default editor as determined by $EDITOR (set via ~/.config/uwsm/default) (or nvim if missing).
# Starts suitable editors in a terminal window and otherwise as a regular application.
nomarchy-cmd-present "$EDITOR" || EDITOR=nvim
case "$EDITOR" in
nvim | vim | nano | micro | hx | helix | fresh)
exec nomarchy-launch-tui "$EDITOR" "$@"
;;
*)
exec setsid uwsm-app -- "$EDITOR" "$@"
;;
esac

View File

@@ -0,0 +1,7 @@
#!/bin/bash
# Launch a floating terminal with the Nomarchy logo presentation, then execute the command passed in, and finally end with the nomarchy-show-done presentation.
# Used by actions such as Update System.
cmd="$*"
exec setsid uwsm-app -- xdg-terminal-exec --app-id=org.nomarchy.terminal --title=Nomarchy -e bash -c "nomarchy-show-logo; $cmd; if (( \$? != 130 )); then nomarchy-show-done; fi"

View File

@@ -0,0 +1,19 @@
#!/bin/bash
# Launch or focus on a given command identified by the passed in window-pattern.
# Use by some default bindings, like the one for Spotify, to ensure there is only one instance of the application open.
if (($# == 0)); then
echo "Usage: nomarchy-launch-or-focus [window-pattern] [launch-command]"
exit 1
fi
WINDOW_PATTERN="$1"
LAUNCH_COMMAND="${2:-"uwsm-app -- $WINDOW_PATTERN"}"
WINDOW_ADDRESS=$(hyprctl clients -j | jq -r --arg p "$WINDOW_PATTERN" '.[]|select((.class|test("\\b" + $p + "\\b";"i")) or (.title|test("\\b" + $p + "\\b";"i")))|.address' | head -n1)
if [[ -n $WINDOW_ADDRESS ]]; then
hyprctl dispatch focuswindow "address:$WINDOW_ADDRESS"
else
eval exec setsid $LAUNCH_COMMAND
fi

View File

@@ -0,0 +1,9 @@
#!/bin/bash
# Launch or focus on a given TUI identified by the passed in as the command.
# Use by commands like nomarchy-launch-wifi to ensure there is only one wifi configuration screen open.
APP_ID="org.nomarchy.$(basename "$1")"
LAUNCH_COMMAND="nomarchy-launch-tui $@"
exec nomarchy-launch-or-focus "$APP_ID" "$LAUNCH_COMMAND"

View File

@@ -0,0 +1,15 @@
#!/bin/bash
# Launch or focus on a given web app identified by the window-pattern.
# Use by some default bindings, like the one for WhatsApp, to ensure there is only one instance of the application open.
if (($# == 0)); then
echo "Usage: nomarchy-launch-or-focus-webapp [window-pattern] [url-and-flags...]"
exit 1
fi
WINDOW_PATTERN="$1"
shift
LAUNCH_COMMAND="nomarchy-launch-webapp $@"
exec nomarchy-launch-or-focus "$WINDOW_PATTERN" "$LAUNCH_COMMAND"

View File

@@ -0,0 +1,56 @@
#!/bin/bash
# Launch the Nomarchy screensaver in the default terminal on the system with the correct font configuration.
# Exit early if we don't have the tte show
if ! command -v tte &>/dev/null; then
exit 1
fi
# Exit early if screensave is already running
pgrep -f org.nomarchy.screensaver && exit 0
# Allow screensaver to be turned off but also force started
# Skip if screensaver is disabled in configuration
if [[ $NNOMARCHY_TOGGLE_SCREENSAVER == "false" ]] && [[ $1 != "force" ]]; then
exit 0
fi
# Silently quit Walker on overlay
walker -q
focused=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true).name')
terminal=$(xdg-terminal-exec --print-id)
for m in $(hyprctl monitors -j | jq -r '.[] | .name'); do
hyprctl dispatch focusmonitor $m
case $terminal in
*Alacritty*)
hyprctl dispatch exec -- \
alacritty --class=org.nomarchy.screensaver \
--config-file ~/.local/share/nomarchy/default/alacritty/screensaver.toml \
-e nomarchy-cmd-screensaver
;;
*ghostty*)
hyprctl dispatch exec -- \
ghostty --class=org.nomarchy.screensaver \
--config-file=~/.local/share/nomarchy/default/ghostty/screensaver \
--font-size=18 \
-e nomarchy-cmd-screensaver
;;
*kitty*)
hyprctl dispatch exec -- \
kitty --class=org.nomarchy.screensaver \
--override font_size=18 \
--override window_padding_width=0 \
-e nomarchy-cmd-screensaver
;;
*)
notify-send -u low "✋ Screensaver only runs in Alacritty, Ghostty, or Kitty"
;;
esac
done
hyprctl dispatch focusmonitor $focused

View File

@@ -0,0 +1,5 @@
#!/bin/bash
# Launch the TUI command passed in as an argument in the default terminal with an org.nomarchy.COMMAND app id for styling.
exec setsid uwsm-app -- xdg-terminal-exec --app-id=org.nomarchy.$(basename $1) -e "$1" "${@:2}"

View File

@@ -0,0 +1,15 @@
#!/bin/bash
# Launch the Walker application launcher while ensuring that it's data provider (called elephant) is running first.
# Ensure elephant is running before launching walker
if ! pgrep -x elephant > /dev/null; then
setsid uwsm-app -- elephant &
fi
# Ensure walker service is running
if ! pgrep -f "walker --gapplication-service" > /dev/null; then
setsid uwsm-app -- walker --gapplication-service &
fi
exec walker --width 644 --maxheight 300 --minheight 300 "$@"

View File

@@ -0,0 +1,12 @@
#!/bin/bash
# Launch the passed in URL as a web app in the default browser (or chromium if the default doesn't support --app).
browser=$(xdg-settings get default-web-browser)
case $browser in
google-chrome* | brave-browser* | microsoft-edge* | opera* | vivaldi* | helium*) ;;
*) browser="chromium.desktop" ;;
esac
exec setsid uwsm-app -- $(sed -n 's/^Exec=\([^ ]*\).*/\1/p' {~/.local,~/.nix-profile,/usr}/share/applications/$browser 2>/dev/null | head -1) --app="$1" "${@:2}"

View File

@@ -0,0 +1,7 @@
#!/bin/bash
# Launch the Nomarchy wifi controls (provided by the Impala TUI).
# Attempts to unblock the wifi service first in case it should be been blocked.
rfkill unblock wifi
nomarchy-launch-or-focus-tui impala

View File

@@ -0,0 +1,7 @@
#!/bin/bash
# Restart an application by killing it and relaunching via uwsm.
# Usage: nomarchy-restart-app <application-name> [application-args...]
pkill -x $1
setsid uwsm-app -- "$@" >/dev/null 2>&1 &

View File

@@ -0,0 +1,5 @@
#!/bin/bash
# Reload btop configuration (used by the Nomarchy theme switching).
pkill -SIGUSR2 btop

View File

@@ -0,0 +1,7 @@
#!/bin/bash
# Reload opencode configuration (used by the Nomarchy theme switching).
if pgrep -x opencode >/dev/null; then
killall -SIGUSR2 opencode
fi

View File

@@ -0,0 +1,13 @@
#!/bin/bash
if [[ -f ~/.config/alacritty/alacritty.toml ]]; then
touch ~/.config/alacritty/alacritty.toml
fi
if pgrep -x kitty >/dev/null; then
killall -SIGUSR1 kitty >/dev/null
fi
if pgrep -x ghostty >/dev/null; then
killall -SIGUSR2 ghostty
fi

View File

@@ -0,0 +1,7 @@
#!/bin/bash
# Restart tmux if running with the latest configuration
if pgrep -x tmux; then
tmux source-file ~/.config/tmux/tmux.conf
fi

View File

@@ -0,0 +1,59 @@
#!/bin/bash
set -e
if (( $# != 4 )); then
echo -e "\e[32mLet's create a TUI shortcut you can start with the app launcher.\n\e[0m"
APP_NAME=$(gum input --prompt "Name> " --placeholder "My TUI")
APP_EXEC=$(gum input --prompt "Launch Command> " --placeholder "lazydocker or bash -c 'dust; read -n 1 -s'")
WINDOW_STYLE=$(gum choose --header "Window style" float tile)
ICON_URL=$(gum input --prompt "Icon URL> " --placeholder "See https://dashboardicons.com (must use PNG or SVG!)")
else
APP_NAME="$1"
APP_EXEC="$2"
WINDOW_STYLE="$3"
ICON_URL="$4"
fi
if [[ -z $APP_NAME || -z $APP_EXEC || -z $ICON_URL ]]; then
echo "You must set app name, app command, and icon URL!"
exit 1
fi
ICON_DIR="$HOME/.local/share/applications/icons"
DESKTOP_FILE="$HOME/.local/share/applications/$APP_NAME.desktop"
if [[ ! $ICON_URL =~ ^https?:// ]] && [[ -f $ICON_URL ]]; then
ICON_PATH="$ICON_URL"
else
ICON_PATH="$ICON_DIR/$APP_NAME.png"
mkdir -p "$ICON_DIR"
if ! curl -sL -o "$ICON_PATH" "$ICON_URL"; then
echo "Error: Failed to download icon."
exit 1
fi
fi
if [[ $WINDOW_STYLE == "float" ]]; then
APP_CLASS="TUI.float"
else
APP_CLASS="TUI.tile"
fi
cat >"$DESKTOP_FILE" <<EOF
[Desktop Entry]
Version=1.0
Name=$APP_NAME
Comment=$APP_NAME
Exec=xdg-terminal-exec --app-id=$APP_CLASS -e $APP_EXEC
Terminal=false
Type=Application
Icon=$ICON_PATH
StartupNotify=true
EOF
chmod +x "$DESKTOP_FILE"
if (( $# != 4 )); then
echo -e "You can now find $APP_NAME using the app launcher (SUPER + SPACE)\n"
fi

View File

@@ -0,0 +1,43 @@
#!/bin/bash
set -e
ICON_DIR="$HOME/.local/share/applications/icons"
DESKTOP_DIR="$HOME/.local/share/applications/"
if (( $# == 0 )); then
# Find all TUIs
while IFS= read -r -d '' file; do
if grep -qE '^Exec=.*(\$TERMINAL|xdg-terminal-exec).*-e' "$file"; then
TUIS+=("$(basename "${file%.desktop}")")
fi
done < <(find "$DESKTOP_DIR" -name '*.desktop' -print0)
if ((${#TUIS[@]})); then
IFS=$'\n' SORTED_TUIS=($(sort <<<"${TUIS[*]}"))
unset IFS
APP_NAMES_STRING=$(gum choose --no-limit --header "Select TUI to remove..." --selected-prefix="✗ " "${SORTED_TUIS[@]}")
# Convert newline-separated string to array
APP_NAMES=()
while IFS= read -r line; do
[[ -n $line ]] && APP_NAMES+=("$line")
done <<< "$APP_NAMES_STRING"
else
echo "No TUIs to remove."
exit 1
fi
else
# Use array to preserve spaces in app names
APP_NAMES=("$@")
fi
if (( ${#APP_NAMES[@]} == 0 )); then
echo "You must provide TUI names."
exit 1
fi
for APP_NAME in "${APP_NAMES[@]}"; do
rm -f "$DESKTOP_DIR/$APP_NAME.desktop"
rm -f "$ICON_DIR/$APP_NAME.png"
echo "Removed $APP_NAME"
done

View File

@@ -0,0 +1,36 @@
#!/bin/bash
# Remove all TUIs installed via nomarchy-tui-install.
# Identifies TUIs by their Exec pattern (xdg-terminal-exec --app-id=TUI.).
set -e
APP_DIR="${1:-$HOME/.local/share/applications}"
ICON_DIR="$HOME/.local/share/applications/icons"
echo "Scanning for TUIs in $APP_DIR..."
tui_desktop_files=()
while IFS= read -r -d '' file; do
if grep -q "Exec=xdg-terminal-exec --app-id=TUI\." "$file" 2>/dev/null; then
tui_desktop_files+=("$file")
fi
done < <(find "$APP_DIR" -maxdepth 1 -name "*.desktop" -print0 2>/dev/null)
if (( ${#tui_desktop_files[@]} == 0 )); then
echo "No TUIs found."
exit 0
fi
for file in "${tui_desktop_files[@]}"; do
app_name=$(basename "$file" .desktop)
echo "Removing TUI: $app_name"
rm -f "$file"
rm -f "$ICON_DIR/$app_name.png"
done
if command -v update-desktop-database &>/dev/null; then
update-desktop-database "$APP_DIR" &>/dev/null || true
fi
echo "TUIs removed successfully."

View File

@@ -0,0 +1,6 @@
#!/bin/bash
set -e
# Used by Voxtype waybar module to open config on right click
exec nomarchy-launch-editor ~/.config/voxtype/config.toml

View File

@@ -0,0 +1,21 @@
#!/bin/bash
set -e
# Install voxtype and configure it for use.
if gum confirm "Install Voxtype + AI model (~150MB) to enable dictation?"; then
nomarchy-pkg-add wtype voxtype-bin
# Setup voxtype
mkdir -p ~/.config/voxtype
cp ~/.config/nomarchy/default/voxtype/config.toml ~/.config/voxtype/
voxtype setup --download --no-post-install
if nomarchy-hw-vulkan; then
voxtype setup gpu --enable || true
fi
voxtype setup systemd
nomarchy-restart-waybar
notify-send " Voxtype Dictation Ready" "Press Super + Ctrl + X to toggle dictation.\nEdit ~/.config/voxtype/config.toml for options." -t 10000
fi

View File

@@ -0,0 +1,5 @@
#!/bin/bash
set -e
nomarchy-launch-floating-terminal-with-presentation "voxtype setup model"
nomarchy-restart-waybar

View File

@@ -0,0 +1,20 @@
#!/bin/bash
set -e
# Remove voxtype and its configurations.
if nomarchy-cmd-present voxtype; then
echo "Uninstall Voxtype to remove dictation."
# Remove services
systemctl --user stop voxtype.service 2>/dev/null || true
rm -f ~/.config/systemd/user/voxtype*
systemctl --user daemon-reload
# Remove packages and configs
nomarchy-pkg-drop wtype voxtype-bin
rm -rf ~/.config/voxtype
rm -rf ~/.local/share/voxtype
else
echo "Voxtype was not installed."
fi

View File

@@ -0,0 +1,9 @@
#!/bin/bash
if nomarchy-cmd-present voxtype; then
voxtype status --follow --extended --format json | while read -r line; do
echo "$line" | jq -c '. + {alt: .class}'
done
else
echo '{"alt": "", "tooltip": ""}'
fi

View File

@@ -0,0 +1,11 @@
#!/bin/bash
url="$1"
web_url="https://app.hey.com"
# Handle mailto: URLs
if [[ $url =~ ^mailto: ]]; then
email=$(echo "$url" | sed 's/mailto://')
web_url="https://app.hey.com/messages/new?to=$email"
fi
exec nomarchy-launch-webapp "$web_url"

View File

@@ -0,0 +1,20 @@
#!/bin/bash
url="$1"
web_url="https://app.zoom.us/wc/home"
if [[ $url =~ ^zoom(mtg|us):// ]]; then
confno=$(echo "$url" | sed -n 's/.*[?&]confno=\([^&]*\).*/\1/p')
if [[ -n $confno ]]; then
pwd=$(echo "$url" | sed -n 's/.*[?&]pwd=\([^&]*\).*/\1/p')
if [[ -n $pwd ]]; then
web_url="https://app.zoom.us/wc/join/$confno?pwd=$pwd"
else
web_url="https://app.zoom.us/wc/join/$confno"
fi
fi
fi
exec nomarchy-launch-webapp "$web_url"

View File

@@ -0,0 +1,89 @@
#!/bin/bash
set -e
ICON_DIR="$HOME/.local/share/applications/icons"
if (( $# < 3 )); then
echo -e "\e[32mLet's create a new web app you can start with the app launcher.\n\e[0m"
APP_NAME=$(gum input --prompt "Name> " --placeholder "My favorite web app")
APP_URL=$(gum input --prompt "URL> " --placeholder "https://example.com")
if [[ ! $APP_URL =~ ^[a-zA-Z][a-zA-Z0-9+.-]*: ]]; then
APP_URL="https://$APP_URL"
fi
# Try to fetch favicon automatically first.
FAVICON_URL="https://www.google.com/s2/favicons?domain=${APP_URL}&sz=128"
mkdir -p "$ICON_DIR"
if curl -fsSL -o "$ICON_DIR/$APP_NAME.png" "$FAVICON_URL" && [[ -s $ICON_DIR/$APP_NAME.png ]]; then
ICON_REF="$APP_NAME.png"
else
ICON_REF=$(gum input --prompt "Icon URL> " --placeholder "Could not fetch favicon automatically. Enter PNG icon URL (see https://dashboardicons.com)")
fi
CUSTOM_EXEC=""
MIME_TYPES=""
INTERACTIVE_MODE=true
else
APP_NAME="$1"
APP_URL="$2"
if [[ ! $APP_URL =~ ^[a-zA-Z][a-zA-Z0-9+.-]*: ]]; then
APP_URL="https://$APP_URL"
fi
ICON_REF="$3"
CUSTOM_EXEC="$4" # Optional custom exec command
MIME_TYPES="$5" # Optional mime types
INTERACTIVE_MODE=false
fi
# Ensure valid execution
if [[ -z $APP_NAME || -z $APP_URL ]]; then
echo "You must set app name and app URL!"
exit 1
fi
# Resolve icon from URL or from a local icon name.
mkdir -p "$ICON_DIR"
if [[ -z $ICON_REF ]]; then
ICON_REF="https://www.google.com/s2/favicons?domain=${APP_URL}&sz=128"
fi
if [[ $ICON_REF =~ ^https?:// ]]; then
ICON_PATH="$ICON_DIR/$APP_NAME.png"
if ! curl -fsSL -o "$ICON_PATH" "$ICON_REF" || [[ ! -s $ICON_PATH ]]; then
echo "Error: Failed to download icon."
exit 1
fi
else
ICON_PATH="$ICON_DIR/$ICON_REF"
fi
# Use custom exec if provided, otherwise default behavior
EXEC_COMMAND="${CUSTOM_EXEC:-nomarchy-launch-webapp $APP_URL}"
# Create application .desktop file
DESKTOP_FILE="$HOME/.local/share/applications/$APP_NAME.desktop"
cat >"$DESKTOP_FILE" <<EOF
[Desktop Entry]
Version=1.0
Name=$APP_NAME
Comment=$APP_NAME
Exec=$EXEC_COMMAND
Terminal=false
Type=Application
Icon=$ICON_PATH
StartupNotify=true
EOF
# Add mime types if provided
if [[ -n $MIME_TYPES ]]; then
echo "MimeType=$MIME_TYPES" >>"$DESKTOP_FILE"
fi
chmod +x "$DESKTOP_FILE"
if [[ $INTERACTIVE_MODE == "true" ]]; then
echo -e "You can now find $APP_NAME using the app launcher (SUPER + SPACE)\n"
fi

View File

@@ -0,0 +1,43 @@
#!/bin/bash
set -e
ICON_DIR="$HOME/.local/share/applications/icons"
DESKTOP_DIR="$HOME/.local/share/applications/"
if (( $# == 0 )); then
# Find all web apps
while IFS= read -r -d '' file; do
if grep -q '^Exec=.*\(nomarchy-launch-webapp\|nomarchy-webapp-handler\).*' "$file"; then
WEB_APPS+=("$(basename "${file%.desktop}")")
fi
done < <(find "$DESKTOP_DIR" -name '*.desktop' -print0)
if ((${#WEB_APPS[@]})); then
IFS=$'\n' SORTED_WEB_APPS=($(sort <<<"${WEB_APPS[*]}"))
unset IFS
APP_NAMES_STRING=$(gum choose --no-limit --header "Select web app to remove..." --selected-prefix="✗ " "${SORTED_WEB_APPS[@]}")
# Convert newline-separated string to array
APP_NAMES=()
while IFS= read -r line; do
[[ -n $line ]] && APP_NAMES+=("$line")
done <<< "$APP_NAMES_STRING"
else
echo "No web apps to remove."
exit 1
fi
else
# Use array to preserve spaces in app names
APP_NAMES=("$@")
fi
if (( ${#APP_NAMES[@]} == 0 )); then
echo "You must select at least one web app to remove."
exit 1
fi
for APP_NAME in "${APP_NAMES[@]}"; do
rm -f "$DESKTOP_DIR/$APP_NAME.desktop"
rm -f "$ICON_DIR/$APP_NAME.png"
echo "Removed $APP_NAME"
done

View File

@@ -0,0 +1,36 @@
#!/bin/bash
# Remove all web apps installed via nomarchy-webapp-install.
# Identifies web apps by their Exec pattern (nomarchy-launch-webapp or nomarchy-webapp-handler).
set -e
APP_DIR="${1:-$HOME/.local/share/applications}"
ICON_DIR="$HOME/.local/share/applications/icons"
echo "Scanning for web apps in $APP_DIR..."
webapp_desktop_files=()
while IFS= read -r -d '' file; do
if grep -q "Exec=nomarchy-launch-webapp\|Exec=nomarchy-webapp-handler" "$file" 2>/dev/null; then
webapp_desktop_files+=("$file")
fi
done < <(find "$APP_DIR" -maxdepth 1 -name "*.desktop" -print0 2>/dev/null)
if (( ${#webapp_desktop_files[@]} == 0 )); then
echo "No web apps found."
exit 0
fi
for file in "${webapp_desktop_files[@]}"; do
app_name=$(basename "$file" .desktop)
echo "Removing web app: $app_name"
rm -f "$file"
rm -f "$ICON_DIR/$app_name.png"
done
if command -v update-desktop-database &>/dev/null; then
update-desktop-database "$APP_DIR" &>/dev/null || true
fi
echo "Web apps removed successfully."

View File

@@ -0,0 +1,448 @@
#!/bin/bash
COMPOSE_FILE="$HOME/.config/windows/docker-compose.yml"
check_prerequisites() {
local DISK_SIZE_GB=${1:-64}
local REQUIRED_SPACE=$((DISK_SIZE_GB + 10)) # Add 10GB for Windows ISO and overhead
# Check for KVM support
if [[ ! -e /dev/kvm ]]; then
gum style \
--border normal \
--padding "1 2" \
--margin "1" \
"❌ KVM virtualization not available!" \
"" \
"Please enable virtualization in BIOS or run:" \
" sudo modprobe kvm-intel # for Intel CPUs" \
" sudo modprobe kvm-amd # for AMD CPUs"
exit 1
fi
# Check disk space
AVAILABLE_SPACE=$(df "$HOME" | awk 'NR==2 {print int($4/1024/1024)}')
if (( AVAILABLE_SPACE < REQUIRED_SPACE )); then
echo "❌ Insufficient disk space!"
echo " Available: ${AVAILABLE_SPACE}GB"
echo " Required: ${REQUIRED_SPACE}GB (${DISK_SIZE_GB}GB disk + 10GB for Windows image)"
exit 1
fi
}
install_windows() {
# Set up trap to handle Ctrl+C
trap "echo ''; echo 'Installation cancelled by user'; exit 1" INT
check_prerequisites
nomarchy-pkg-add freerdp openbsd-netcat gum
mkdir -p "$HOME/.windows"
mkdir -p "$HOME/.config/windows"
mkdir -p "$HOME/.local/share/applications/icons"
# Install Windows VM icon and desktop file
if [[ -f $NOMARCHY_PATH/applications/icons/windows.png ]]; then
cp "$NOMARCHY_PATH/applications/icons/windows.png" "$HOME/.local/share/applications/icons/windows.png"
fi
cat << EOF | tee "$HOME/.local/share/applications/windows-vm.desktop" > /dev/null
[Desktop Entry]
Name=Windows
Comment=Start Windows VM via Docker and connect with RDP
Exec=uwsm app -- nomarchy-windows-vm launch
Icon=$HOME/.local/share/applications/icons/windows.png
Terminal=false
Type=Application
Categories=System;Virtualization;
EOF
# Get system resources
TOTAL_RAM=$(free -h | awk 'NR==2 {print $2}')
TOTAL_RAM_GB=$(awk 'NR==1 {printf "%d", $2/1024/1024}' /proc/meminfo)
TOTAL_CORES=$(nproc)
echo ""
echo "System Resources Detected:"
echo " Total RAM: $TOTAL_RAM"
echo " Total CPU Cores: $TOTAL_CORES"
echo ""
RAM_OPTIONS=""
for size in 2 4 8 16 32 64; do
if (( size <= TOTAL_RAM_GB )); then
RAM_OPTIONS="$RAM_OPTIONS ${size}G"
fi
done
SELECTED_RAM=$(echo $RAM_OPTIONS | tr ' ' '\n' | gum choose --selected="4G" --header="How much RAM would you like to allocate to Windows VM?")
# Check if user cancelled
if [[ -z $SELECTED_RAM ]]; then
echo "Installation cancelled by user"
exit 1
fi
SELECTED_CORES=$(gum input --placeholder="Number of CPU cores (1-$TOTAL_CORES)" --value="2" --header="How many CPU cores would you like to allocate to Windows VM?" --char-limit=2)
# Check if user cancelled (Ctrl+C in gum input returns empty string)
if [[ -z $SELECTED_CORES ]]; then
echo "Installation cancelled by user"
exit 1
fi
if ! [[ $SELECTED_CORES =~ ^[0-9]+$ ]] || (( SELECTED_CORES < 1 )) || (( SELECTED_CORES > TOTAL_CORES )); then
echo "Invalid input. Using default: 2 cores"
SELECTED_CORES=2
fi
AVAILABLE_SPACE=$(df "$HOME" | awk 'NR==2 {print int($4/1024/1024)}')
MAX_DISK_GB=$((AVAILABLE_SPACE - 10)) # Leave 10GB for Windows image
# Check if we have enough space for minimum
if (( MAX_DISK_GB < 32 )); then
echo "❌ Insufficient disk space for Windows VM!"
echo " Available: ${AVAILABLE_SPACE}GB"
echo " Minimum required: 42GB (32GB disk + 10GB for Windows image)"
exit 1
fi
DISK_OPTIONS=""
for size in 32 64 128 256 512; do
if (( size <= MAX_DISK_GB )); then
DISK_OPTIONS="$DISK_OPTIONS ${size}G"
fi
done
# Default to 64G if available, otherwise 32G
DEFAULT_DISK="64G"
if ! echo "$DISK_OPTIONS" | grep -q "64G"; then
DEFAULT_DISK="32G"
fi
SELECTED_DISK=$(echo $DISK_OPTIONS | tr ' ' '\n' | gum choose --selected="$DEFAULT_DISK" --header="How much disk space would you like to give Windows VM? (64GB+ recommended)")
# Check if user cancelled
if [[ -z $SELECTED_DISK ]]; then
echo "Installation cancelled by user"
exit 1
fi
# Extract just the number for prerequisite check
DISK_SIZE_NUM=$(echo "$SELECTED_DISK" | sed 's/G//')
# Re-check prerequisites with selected disk size
check_prerequisites "$DISK_SIZE_NUM"
# Prompt for username and password
USERNAME=$(gum input --placeholder="Username (Press enter to use default: docker)" --header="Enter Windows username:")
if [[ -z $USERNAME ]]; then
USERNAME="docker"
fi
PASSWORD=$(gum input --placeholder="Password (Press enter to use default: admin)" --password --header="Enter Windows password:")
if [[ -z $PASSWORD ]]; then
PASSWORD="admin"
PASSWORD_DISPLAY="(default)"
else
PASSWORD_DISPLAY="(user-defined)"
fi
# Display configuration summary
gum style \
--border normal \
--padding "1 2" \
--margin "1" \
--align left \
--bold \
"Windows VM Configuration" \
"" \
"RAM: $SELECTED_RAM" \
"CPU: $SELECTED_CORES cores" \
"Disk: $SELECTED_DISK" \
"Username: $USERNAME" \
"Password: $PASSWORD_DISPLAY"
# Ask for confirmation
echo ""
if ! gum confirm "Proceed with this configuration?"; then
echo "Installation cancelled by user"
exit 1
fi
mkdir -p $HOME/Windows
# Create docker-compose.yml in user config directory
cat << EOF | tee "$COMPOSE_FILE" > /dev/null
services:
windows:
image: dockurr/windows
container_name: nomarchy-windows
environment:
VERSION: "11"
RAM_SIZE: "$SELECTED_RAM"
CPU_CORES: "$SELECTED_CORES"
DISK_SIZE: "$SELECTED_DISK"
USERNAME: "$USERNAME"
PASSWORD: "$PASSWORD"
TZ: "$(timedatectl show -p Timezone --value 2>/dev/null || echo UTC)"
ARGUMENTS: "-rtc base=localtime,clock=host,driftfix=slew"
devices:
- /dev/kvm
- /dev/net/tun
cap_add:
- NET_ADMIN
ports:
- 127.0.0.1:8006:8006
- 127.0.0.1:3389:3389/tcp
- 127.0.0.1:3389:3389/udp
volumes:
- $HOME/.windows:/storage
- $HOME/Windows:/shared
restart: unless-stopped
stop_grace_period: 2m
EOF
echo ""
echo "Starting Windows VM installation..."
echo "This will download a Windows 11 image (may take 10-15 minutes)."
echo ""
echo "Monitor installation progress at: http://127.0.0.1:8006"
echo ""
# Start docker-compose with user's config
echo "Starting Windows VM with docker-compose..."
if ! docker-compose -f "$COMPOSE_FILE" up -d 2>&1; then
echo "❌ Failed to start Windows VM!"
echo " Common issues:"
echo " - Docker daemon not running: sudo systemctl start docker"
echo " - Port already in use: check if another VM is running"
echo " - Permission issues: make sure you're in the docker group"
exit 1
fi
echo ""
echo "Windows VM is starting up!"
echo ""
echo "Opening browser to monitor installation..."
# Open browser to monitor installation
sleep 3
xdg-open "http://127.0.0.1:8006"
echo ""
echo "Installation is running in the background."
echo "You can monitor progress at: http://127.0.0.1:8006"
echo ""
echo "Once finished, launch 'Windows' via Super + Space"
echo ""
echo "To stop the VM: nomarchy-windows-vm stop"
echo "To change resources: ~/.config/windows/docker-compose.yml"
echo ""
}
remove_windows() {
if ! gum confirm --default=false "Remove Windows VM and delete all associated data?"; then
echo "Removal cancelled by user"
exit 1
fi
echo "Removing Windows VM..."
docker-compose -f "$COMPOSE_FILE" down 2>/dev/null || true
docker rmi dockurr/windows 2>/dev/null || echo "Image already removed or not found"
rm "$HOME/.local/share/applications/windows-vm.desktop"
rm -rf "$HOME/.config/windows"
rm -rf "$HOME/.windows"
echo ""
echo "Windows VM removal completed!"
}
launch_windows() {
KEEP_ALIVE=false
if [[ $1 = "--keep-alive" ]] || [[ $1 = "-k" ]]; then
KEEP_ALIVE=true
fi
# Check if config exists
if [[ ! -f $COMPOSE_FILE ]]; then
echo "Windows VM not configured. Please run: nomarchy-windows-vm install"
exit 1
fi
# Extract credentials from compose file
WIN_USER=$(grep "USERNAME:" "$COMPOSE_FILE" | sed 's/.*USERNAME: "\(.*\)"/\1/')
WIN_PASS=$(grep "PASSWORD:" "$COMPOSE_FILE" | sed 's/.*PASSWORD: "\(.*\)"/\1/')
# Use defaults if not found
[[ -z $WIN_USER ]] && WIN_USER="docker"
[[ -z $WIN_PASS ]] && WIN_PASS="admin"
# Check if container is already running
CONTAINER_STATUS=$(docker inspect --format='{{.State.Status}}' nomarchy-windows 2>/dev/null)
if [[ $CONTAINER_STATUS != "running" ]]; then
echo "Starting Windows VM..."
# Send desktop notification
notify-send " Starting Windows VM" " This can take 15-30 seconds" -t 15000
if ! docker-compose -f "$COMPOSE_FILE" up -d 2>&1; then
echo "❌ Failed to start Windows VM!"
echo " Try checking: nomarchy-windows-vm status"
echo " View logs: docker logs nomarchy-windows"
notify-send -u critical "Windows VM" "Failed to start Windows VM"
exit 1
fi
echo "Waiting for Windows VM to start..."
WAIT_COUNT=0
until docker logs nomarchy-windows 2>&1 | grep -qi "windows started successfully"; do
sleep 2
WAIT_COUNT=$((WAIT_COUNT + 1))
if (( WAIT_COUNT > 60 )); then # 2 minutes timeout
echo ""
echo "❌ Timeout: Windows VM failed to start within 2 minutes"
echo " Check logs: docker logs nomarchy-windows"
exit 1
fi
done
fi
# Build the connection info
if [[ $KEEP_ALIVE = "true" ]]; then
LIFECYCLE="VM will keep running after RDP closes
To stop: nomarchy-windows-vm stop"
else
LIFECYCLE="VM will auto-stop when RDP closes"
fi
gum style \
--border normal \
--padding "1 2" \
--margin "1" \
--align center \
"Connecting to Windows VM" \
"" \
"$LIFECYCLE"
# Detect display scale from Hyprland
HYPR_SCALE=$(hyprctl monitors -j | jq -r '.[] | select (.focused == true) | .scale')
SCALE_PERCENT=$(echo "$HYPR_SCALE" | awk '{print int($1 * 100)}')
RDP_SCALE=""
if (( SCALE_PERCENT >= 170 )); then
RDP_SCALE="/scale:180"
elif (( SCALE_PERCENT >= 130 )); then
RDP_SCALE="/scale:140"
fi
# If scale is less than 130%, don't set any scale (use default 100)
# Connect with RDP in fullscreen (auto-detects resolution)
xfreerdp3 /u:"$WIN_USER" /p:"$WIN_PASS" /v:127.0.0.1:3389 -grab-keyboard /sound /microphone /clipboard /cert:ignore /title:"Windows VM - Nomarchy" /dynamic-resolution /gfx:AVC444 /floatbar:sticky:off,default:visible,show:fullscreen $RDP_SCALE
# After RDP closes, stop the container unless --keep-alive was specified
if [[ $KEEP_ALIVE = "false" ]]; then
echo ""
echo "RDP session closed. Stopping Windows VM..."
docker-compose -f "$COMPOSE_FILE" down
echo "Windows VM stopped."
else
echo ""
echo "RDP session closed. Windows VM is still running."
echo "To stop it: nomarchy-windows-vm stop"
fi
}
stop_windows() {
if [[ ! -f $COMPOSE_FILE ]]; then
echo "Windows VM not configured."
exit 1
fi
echo "Stopping Windows VM..."
docker-compose -f "$COMPOSE_FILE" down
echo "Windows VM stopped."
}
status_windows() {
if [[ ! -f $COMPOSE_FILE ]]; then
echo "Windows VM not configured."
echo "To set up: nomarchy-windows-vm install"
exit 1
fi
CONTAINER_STATUS=$(docker inspect --format='{{.State.Status}}' nomarchy-windows 2>/dev/null)
if [[ -z $CONTAINER_STATUS ]]; then
echo "Windows VM container not found."
echo "To start: nomarchy-windows-vm launch"
elif [[ $CONTAINER_STATUS = "running" ]]; then
gum style \
--border normal \
--padding "1 2" \
--margin "1" \
--align left \
"Windows VM Status: RUNNING" \
"" \
"Web interface: http://127.0.0.1:8006" \
"RDP available: port 3389" \
"" \
"To connect: nomarchy-windows-vm launch" \
"To stop: nomarchy-windows-vm stop"
else
echo "Windows VM is stopped (status: $CONTAINER_STATUS)"
echo "To start: nomarchy-windows-vm launch"
fi
}
show_usage() {
echo "Usage: nomarchy-windows-vm [command] [options]"
echo ""
echo "Commands:"
echo " install Install and configure Windows VM"
echo " remove Remove Windows VM and optionally its data"
echo " launch [options] Start Windows VM (if needed) and connect via RDP"
echo " Options:"
echo " --keep-alive, -k Keep VM running after RDP closes"
echo " stop Stop the running Windows VM"
echo " status Show current VM status"
echo " help Show this help message"
echo ""
echo "Examples:"
echo " nomarchy-windows-vm install # Set up Windows VM for first time"
echo " nomarchy-windows-vm launch # Connect to VM (auto-stop on exit)"
echo " nomarchy-windows-vm launch -k # Connect to VM (keep running)"
echo " nomarchy-windows-vm stop # Shut down the VM"
}
# Main command dispatcher
case "$1" in
install)
install_windows
;;
remove)
remove_windows
;;
launch|start)
launch_windows "$2"
;;
stop|down)
stop_windows
;;
status)
status_windows
;;
help|--help|-h|"")
show_usage
;;
*)
echo "Unknown command: $1" >&2
echo "" >&2
show_usage >&2
exit 1
;;
esac

41
features/apps/swayosd.nix Normal file
View File

@@ -0,0 +1,41 @@
{ config, pkgs, lib, ... }:
{
services.swayosd = {
enable = lib.mkDefault true;
stylePath = lib.mkDefault "${config.home.homeDirectory}/.config/swayosd/style.css";
};
xdg.configFile."swayosd/style.css".text = lib.mkDefault ''
@define-color background-color #${config.colorScheme.palette.base00};
@define-color border-color #${config.colorScheme.palette.base0E};
@define-color label #${config.colorScheme.palette.base05};
@define-color image #${config.colorScheme.palette.base05};
@define-color progress #${config.colorScheme.palette.base0B};
window {
border-radius: 0;
opacity: 0.97;
border: 2px solid @border-color;
background-color: @background-color;
}
label {
font-family: '${config.nomarchy.fonts.monospace}';
font-size: 11pt;
color: @label;
}
image {
color: @image;
}
progressbar {
border-radius: 0;
}
progress {
background-color: @progress;
}
'';
}

View File

@@ -0,0 +1,94 @@
# Prefix
set -g prefix C-Space
set -g prefix2 C-b
bind C-Space send-prefix
# Reload config
bind q source-file ~/.config/tmux/tmux.conf \; display "Configuration reloaded"
# Vi mode for copy
setw -g mode-keys vi
bind -T copy-mode-vi v send -X begin-selection
bind -T copy-mode-vi y send -X copy-selection-and-cancel
# Pane Controls
bind h split-window -v -c "#{pane_current_path}"
bind v split-window -h -c "#{pane_current_path}"
bind x kill-pane
bind -n C-M-Left select-pane -L
bind -n C-M-Right select-pane -R
bind -n C-M-Up select-pane -U
bind -n C-M-Down select-pane -D
bind -n C-M-S-Left resize-pane -L 5
bind -n C-M-S-Down resize-pane -D 5
bind -n C-M-S-Up resize-pane -U 5
bind -n C-M-S-Right resize-pane -R 5
# Window navigation
bind r command-prompt -I "#W" "rename-window -- '%%'"
bind c new-window -c "#{pane_current_path}"
bind k kill-window
bind -n M-1 select-window -t 1
bind -n M-2 select-window -t 2
bind -n M-3 select-window -t 3
bind -n M-4 select-window -t 4
bind -n M-5 select-window -t 5
bind -n M-6 select-window -t 6
bind -n M-7 select-window -t 7
bind -n M-8 select-window -t 8
bind -n M-9 select-window -t 9
bind -n M-Left select-window -t -1
bind -n M-Right select-window -t +1
bind -n M-S-Left swap-window -t -1 \; select-window -t -1
bind -n M-S-Right swap-window -t +1 \; select-window -t +1
# Session controls
bind R command-prompt -I "#S" "rename-session -- '%%'"
bind C new-session -c "#{pane_current_path}"
bind K kill-session
bind P switch-client -p
bind N switch-client -n
bind -n M-Up switch-client -p
bind -n M-Down switch-client -n
# General
set -g default-terminal "tmux-256color"
set -ag terminal-overrides ",*:RGB"
set -g mouse on
set -g base-index 1
setw -g pane-base-index 1
set -g renumber-windows on
set -g history-limit 50000
set -g escape-time 0
set -g focus-events on
set -g set-clipboard on
set -g allow-passthrough on
setw -g aggressive-resize on
set -g detach-on-destroy off
# Status bar
set -g status-position top
set -g status-interval 5
set -g status-left-length 30
set -g status-right-length 50
set -g window-status-separator ""
set -gw automatic-rename on
set -gw automatic-rename-format '#{b:pane_current_path}'
# Theme
set -g status-style "bg=default,fg=default"
set -g status-left "#[fg=black,bg=blue,bold] #S #[bg=default] "
set -g status-right "#[fg=blue]#{?pane_in_mode,COPY ,}#{?client_prefix,PREFIX ,}#{?window_zoomed_flag,ZOOM ,}#[fg=brightblack]#h "
set -g window-status-format "#[fg=brightblack] #I:#W "
set -g window-status-current-format "#[fg=blue,bold] #I:#W "
set -g pane-border-style "fg=brightblack"
set -g pane-active-border-style "fg=blue"
set -g message-style "bg=default,fg=blue"
set -g message-command-style "bg=default,fg=blue"
set -g mode-style "bg=blue,fg=black"
setw -g clock-mode-colour blue

53
features/apps/vscode.nix Normal file
View File

@@ -0,0 +1,53 @@
{ config, pkgs, lib, ... }:
let
themeConfig = builtins.fromJSON (builtins.readFile (../../assets/themes + "/${config.nomarchy.theme}/vscode.json"));
# Development extensions that match the system theme
devExtensions = with pkgs.vscode-extensions; [
# Language support
ms-python.python
rust-lang.rust-analyzer
golang.go
jnoortheen.nix-ide
# Git integration
eamodio.gitlens
# Editor enhancements
esbenp.prettier-vscode
dbaeumer.vscode-eslint
bradlc.vscode-tailwindcss
# Theme extensions (provide color themes matching nomarchy palettes)
catppuccin.catppuccin-vsc
enkia.tokyo-night
arcticicestudio.nord-visual-studio-code
sainnhe.everforest
mvllow.rose-pine
];
in
{
options.nomarchy.vscode = {
devExtensions = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to install development extensions for VSCode.";
};
};
config = {
programs.vscode = {
enable = lib.mkDefault true;
package = lib.mkDefault pkgs.vscode;
userSettings = lib.mkDefault {
"update.mode" = "none";
"workbench.colorTheme" = themeConfig.name;
"window.titleBarStyle" = "custom";
"editor.fontFamily" = "'${config.nomarchy.fonts.monospace}', 'Droid Sans Mono', monospace";
"terminal.integrated.fontFamily" = config.nomarchy.fonts.monospace;
};
extensions = lib.mkIf config.nomarchy.vscode.devExtensions (lib.mkDefault devExtensions);
};
};
}

56
features/apps/walker.nix Normal file
View File

@@ -0,0 +1,56 @@
{ config, pkgs, lib, ... }:
{
programs.walker = {
enable = lib.mkDefault true;
runAsService = lib.mkDefault true;
config = lib.mkDefault {
theme = "nomarchy";
ui = {
anchors = {
top = true;
};
};
selection_wrap = true;
hide_action_hints = true;
placeholders = {
"default" = { input = " Search..."; list = "No Results"; };
};
keybinds = {
quick_activate = [];
};
columns = {
symbols = 1;
};
providers = {
max_results = 256;
default = [
"desktopapplications"
"websearch"
];
prefixes = [
{ prefix = "/"; provider = "providerlist"; }
{ prefix = "."; provider = "files"; }
{ prefix = ":"; provider = "symbols"; }
{ prefix = "="; provider = "calc"; }
{ prefix = "@"; provider = "websearch"; }
{ prefix = "$"; provider = "clipboard"; }
];
};
};
themes."nomarchy" = lib.mkDefault {
style = ''
* {
color: #${config.colorScheme.palette.base05};
}
#window {
background-color: #${config.colorScheme.palette.base00};
}
.item.active {
background-color: #${config.colorScheme.palette.base03};
color: #${config.colorScheme.palette.base0B};
}
'';
};
};
}