commit f1df2dd897bca21a8936b20cf88896107add5980 Author: Tomas Mirchev Date: Sat Feb 14 08:35:47 2026 +0200 migration diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..29fba01 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +linked.json +.flow-state + +# local/editor noise +.DS_Store +Thumbs.db +.vscode/ +.idea/ +*.swp +*.swo +*~ diff --git a/README.md b/README.md new file mode 100644 index 0000000..030b8f5 --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +# dotfiles_v2 + +Dotfiles repo migrated from `dotfiles/` to the current `flow-cli` layout. + +## Key layout rules + +- `_shared//...` is linked for every profile. +- `//...` is linked only for that profile. +- A package-local `_root/` maps to absolute paths via sudo. + - Example: `_shared/dnsmasq/_root/opt/homebrew/etc/dnsmasq.conf` -> + `/opt/homebrew/etc/dnsmasq.conf` +- Package target paths are home-relative from package root. + - Example: `_shared/bin/.bin/flow` -> `~/.bin/flow` + +## Flow config + +Config files live at: + +- `_shared/flow/.config/flow/config.yaml` +- `_shared/flow/.config/flow/packages.yaml` +- `_shared/flow/.config/flow/profiles.yaml` + +## External modules (submodule-style packages) + +This repo uses flow module packages for shared components: + +- `_shared/nvim/_module.yaml` -> `git@gitea.tomastm.com:tomas.mirchev/nvim-config.git` +- `_shared/barg/_module.yaml` -> `git@gitea.tomastm.com:tomas.mirchev/barg-parser.git` + +Module packages are refreshed by flow on `dotfiles init` and `dotfiles sync`. + +Useful commands: + +```bash +flow dotfiles modules list +flow dotfiles modules sync +``` + +## Profiles + +- `macos` +- `linux` + +## Quick start + +```bash +flow dotfiles init --repo /absolute/path/to/dotfiles_v2 +flow dotfiles modules sync +flow dotfiles link --profile macos +flow bootstrap run --profile macos --var HOSTNAME=mymac --var USER_EMAIL=you@example.com --dry-run +``` + +## Migrated packages from original dotfiles + +- Shared: `bin`, `git`, `gitignore`, `zsh`, `tmux`, `htop`, `addpaths`, `dnsmasq`, `nvim` (module), + `barg` (module) +- macOS: `ghostty`, `wezterm`, `kitty`, `alacritty`, `karabiner`, `linearmouse`, `rectangle`, + `borders`, `sol`, `homebrew`, `setup` +- Linux: `kwin` + +## Notes + +- `flow-cli` treats target conflicts as errors (including `_shared` vs profile), so package paths + must stay unique. diff --git a/_shared/addpaths/.local/bin/addpaths b/_shared/addpaths/.local/bin/addpaths new file mode 100755 index 0000000..f9325a5 --- /dev/null +++ b/_shared/addpaths/.local/bin/addpaths @@ -0,0 +1,106 @@ +#!/usr/bin/env bash + +set -euo pipefail + +usage() { + echo "Usage: $(basename "$0") -c COMMENT_SYMBOL [-e EXCLUDE_PATTERN]... TARGET" + echo " -c Comment symbol (e.g., '#' or '//')" + echo " -e Exclude pattern (can be specified multiple times)" + echo " TARGET File or directory to process" + exit 1 +} + +comment_sym="" +excludes=() + +while getopts "c:e:h" opt; do + case $opt in + c) comment_sym="$OPTARG" ;; + e) excludes+=("$OPTARG") ;; + h) usage ;; + *) usage ;; + esac +done + +shift $((OPTIND - 1)) +[[ $# -ne 1 ]] && usage +[[ -z "$comment_sym" ]] && usage + +target="$(realpath "$1")" +base_dir="$(pwd)" + +process_file() { + local file="$1" + # shellcheck disable=SC2295 + local rel_path="${file#$base_dir/}" + + local path_comment="$comment_sym path: $rel_path" + + # Read first two lines + local line1 line2 + IFS= read -r line1 <"$file" 2>/dev/null || line1="" + IFS= read -r line2 < <(tail -n +2 "$file") 2>/dev/null || line2="" + + # Handle shebang case + if [[ "$line1" =~ ^#! ]]; then + if [[ "$line2" == *"path: "* ]]; then + # Replace existing path comment on line 2 + { + echo "$line1" + echo "$path_comment" + tail -n +3 "$file" + } >"$file.tmp" + else + # Insert new path comment after shebang + { + echo "$line1" + echo "$path_comment" + tail -n +2 "$file" + } >"$file.tmp" + fi + else + if [[ "$line1" == *"path: "* ]]; then + # Replace existing path comment on line 1 + { + echo "$path_comment" + tail -n +2 "$file" + } >"$file.tmp" + else + # Insert new path comment at top + { + echo "$path_comment" + cat "$file" + } >"$file.tmp" + fi + fi + + mv "$file.tmp" "$file" +} + +if [[ -f "$target" ]]; then + process_file "$target" +elif [[ -d "$target" ]]; then + find_cmd=(find "$target") + + # Always exclude hidden files and directories + find_cmd+=(\( -name ".*" -prune \)) + + if [[ ${#excludes[@]} -gt 0 ]]; then + find_cmd+=(-o \() + for i in "${!excludes[@]}"; do + [[ $i -gt 0 ]] && find_cmd+=(-o) + find_cmd+=(-path "*/${excludes[$i]}" -prune) + find_cmd+=(-o -path "*/${excludes[$i]}/*" -prune) + done + find_cmd+=(\)) + fi + + find_cmd+=(-o -type f -print0) + + while IFS= read -r -d '' file; do + process_file "$file" + done < <("${find_cmd[@]}") +else + echo "Error: $target is not a file or directory" >&2 + exit 1 +fi diff --git a/_shared/barg/_module.yaml b/_shared/barg/_module.yaml new file mode 100644 index 0000000..c41e98b --- /dev/null +++ b/_shared/barg/_module.yaml @@ -0,0 +1,3 @@ +source: git@gitea.tomastm.com:tomas.mirchev/barg-parser.git +ref: + branch: main diff --git a/_shared/bin/.bin/dev-tmux-wrapper b/_shared/bin/.bin/dev-tmux-wrapper new file mode 100755 index 0000000..0e3332d --- /dev/null +++ b/_shared/bin/.bin/dev-tmux-wrapper @@ -0,0 +1,12 @@ +#!/bin/bash + +"$HOME/.bin/flow" "$@" 2>&1 +exit_code=$? + +if [ $exit_code -ne 0 ]; then + echo "" + echo "Command: $*" + echo "Failed: exit $exit_code" +fi + +exit $exit_code diff --git a/_shared/bin/.bin/flow b/_shared/bin/.bin/flow new file mode 100755 index 0000000..2bf05d9 --- /dev/null +++ b/_shared/bin/.bin/flow @@ -0,0 +1,613 @@ +#!/usr/bin/env bash + +set -e + +source "$HOME/.bin/barg" + +# shellcheck disable=SC2034 +SPEC=( + "command;flow;DevFlow CLI - Manage instances and development containers" + "note;Use 'flow --help' for command-specific options" + + "command;enter;Connect to a development instance via SSH" + "note;Target format: [user@]namespace@platform (e.g., 'personal@orb' or 'root@personal@orb')" + "argument;user,u;type:option;help:SSH user (overrides user in target)" + "argument;namespace,n;type:option;help:Instance namespace (overrides namespace in target)" + "argument;platform,p;type:option;help:Platform name (overrides platform in target)" + "argument;session,s;type:option;default:default;help:Development session name (default: 'default')" + "argument;no-tmux;type:flag;dest:no_tmux;default:false;help:Skip tmux attachment on connection" + "argument;dry-run,d;type:flag;dest:dry_run;default:false;help:Show SSH command without executing" + "argument;target,t;required;help:Target instance in format [user@]namespace@platform" + "argument;ssh-args;type:rest;dest:ssh_args;help:Additional SSH arguments (after --)" + "end" + + "command;sync;Git tools" + "command;check;Check all projects status" + "end" + "end" + + "command;dotfiles;Manage repository dotfiles and git submodules" + "command;init;Initialize and set up all submodules" + "note;Equivalent to: git submodule update --init --recursive" + "end" + "command;pull;Update all submodules to latest remote commits" + "note;Equivalent to: git submodule update --remote --recursive" + "end" + "command;urls;Synchronize submodule URLs" + "note;Equivalent to: git submodule sync --recursive" + "end" + "command;reset;Reset submodules to the recorded commits" + "note;Equivalent to: git submodule update --init --recursive --force" + "end" + "command;status;Show current submodule status" + "note;Equivalent to: git submodule status --recursive" + "end" + "command;all;Run URLs sync, initialization, and remote update in one step" + "note;Equivalent to: git submodule sync --recursive && git submodule update --init --recursive && git submodule update --remote --recursive" + "end" + "end" + + "command;create;Create and start a new development container" + "argument;image,i;required;type:option;help:Container image to use (with optional tag)" + "argument;project,p;type:option;help:Path to local project directory" + "argument;name;required;help:Container name" + "end" + + "command;exec;Execute a command or open a shell in a container" + "argument;name;required;help:Container name" + "argument;cmd;type:rest;help:Command to execute inside container (after --)" + "end" + + "command;connect;Attach or switch to the container’s tmux session" + "note;When already inside tmux, switches to the target session instead of reattaching." + "note;New tmux panes or windows in the session automatically start inside the container." + "argument;from,f;type:option;dest:name;help:Optional source container name" + "argument;name;required;help:Target container name" + "end" + + "command;list;Display all development containers and their status" + "end" + + "command;stop;Stop or kill a running development container" + "argument;from;type:option;dest:name;help:Optional source container name" + "argument;kill;type:flag;help:Use kill instead of graceful stop" + "argument;name;required;help:Target container name" + "end" + + "command;remove,rm;Remove a development container" + "argument;from;type:option;dest:name;help:Optional source container name" + "argument;force,f;type:flag;help:Force removal of container" + "argument;name;required;help:Target container name" + "end" + + "command;respawn;Restart all tmux panes for a development session" + "argument;from;type:option;dest:name;help:Optional source container name" + "argument;name;required;help:Session or container name" + "end" + + "command;test;Verify that the dev script is functioning" + "argument;from;type:option;dest:name;help:Optional source container name" + "argument;name;help:Target container name" + "end" + + "end" +) + +DEFAULT_REGISTRY="registry.tomastm.com" +DEFAULT_TAG="latest" +PROJECT_DIR="$HOME/projects" +PROJECT_ABBR="p" +CONTAINER_HOME="/home/dev" + +fail() { + printf 'Error: %b\n' "$*" >&2 + exit 1 +} + +resolve_path() { + local path="${1:-$(dirname "${BASH_SOURCE[0]}")}" + if command -v realpath >/dev/null 2>&1; then + realpath "$path" + else + echo "$(cd "$(dirname "$path")" && pwd)/$(basename "$path")" + fi +} + +# shellcheck disable=SC2178,SC2128 +parse_image_ref() { + local input="$1" + + local image_ref registry repo tag label + + if [[ $input == */* ]]; then + local prefix="${input%%/*}" + if [[ "$prefix" == "docker" ]]; then + input="docker.io/library/${input#*/}" + elif [[ "$prefix" == "tm0" ]]; then + input="${DEFAULT_REGISTRY}/${input#*/}" + fi + + registry="${input%%/*}" + input=${input#*/} + else + registry="$DEFAULT_REGISTRY" + fi + + if [[ "${input##*/}" == *:* ]]; then + tag="${input##*:}" + input="${input%:*}" + else + tag="$DEFAULT_TAG" + fi + + repo="${registry}/${input}" + repo="${repo#*/}" + image_ref="${registry}/${repo}:${tag}" + + label="${registry%.*}" + label="${label##*.}/${repo##*/}" + + echo "$image_ref $repo $tag $label" +} + +# shellcheck disable=SC2154,SC2155 +docker_container_exists() { + local cname="$(get_cname)" + docker container ls -a --format '{{.Names}}' | grep -Fqx "$cname" +} + +# shellcheck disable=SC2154,SC2155 +docker_container_running() { + local cname="$(get_cname)" + docker container ls --format '{{.Names}}' | grep -Fqx "$cname" +} + +docker_image_present() { + local ref="$1" + docker image inspect "$ref" >/dev/null 2>&1 +} + +# shellcheck disable=SC2154,SC2155 +get_cname() { + printf "%s" "dev-${name_arg#dev-}" +} + +cmd() { + barg_usage +} + +cmd_dotfiles_init() { + echo "[dotfiles] Initializing submodules..." + git submodule update --init --recursive +} + +cmd_dotfiles_pull() { + echo "[dotfiles] Updating submodules to latest remote commits..." + git submodule update --remote --recursive +} + +cmd_dotfiles_urls() { + echo "[dotfiles] Syncing submodule URLs..." + git submodule sync --recursive +} + +cmd_dotfiles_reset() { + echo "[dotfiles] Resetting submodules to recorded commits..." + git submodule update --init --recursive --force +} + +cmd_dotfiles_status() { + echo "[dotfiles] Submodule status:" + git submodule status --recursive +} + +cmd_dotfiles_all() { + echo "[dotfiles] Syncing URLs..." + git submodule sync --recursive + + echo "[dotfiles] Initializing submodules..." + git submodule update --init --recursive + + echo "[dotfiles] Updating submodules to latest remote commits..." + git submodule update --remote --recursive +} + +# shellcheck disable=SC2154,SC2155 +cmd_enter() { + # VARS: user_arg, namespace_arg, platform_arg, target_arg, session_arg, no_tmux_arg, dry_run_arg, ssh_args_arg + + # Do not run inside instance + if [[ -n "$DF_NAMESPACE" && -n "$DF_PLATFORM" ]]; then + fail "It is not recommended to run this command inside an instance.\nCurrently inside: $(tput bold)${DF_NAMESPACE}@${DF_PLATFORM}$(tput sgr0)" + fi + + local -A CONFIG_HOST=( + [orb.host]="@orb" + [utm.host]=".utm.local" + [core.host]=".core.lan" + ) + + local df_platform="" + local df_namespace="" + local df_user="" + + # Parse target: get user, namespace, platform + if [[ "$target_arg" == *@* ]]; then + df_platform="${target_arg##*@}" + target_arg="${target_arg%@*}" + fi + + if [[ "$target_arg" == *@* ]]; then + df_namespace="${target_arg##*@}" + df_user="${target_arg%@*}" + else + df_namespace="${target_arg}" + df_user="${USER}" + fi + + if [[ -n "$platform_arg" ]]; then + df_platform="$platform_arg" + fi + if [[ -n "$namespace_arg" ]]; then + df_namespace="$namespace_arg" + fi + if [[ -n "$user_arg" ]]; then + df_user="$user_arg" + fi + + # Resolve host, identity (maybe check what would the host be in order to use .ssh/config) + local host_config="${CONFIG_HOST[${df_platform}.host]}" + local ssh_host="${host_config///$df_namespace}" + if [[ -z "$ssh_host" ]]; then + fail "Invalid platform: ${df_platform}" + fi + + # Build ssh cmd: ssh + identity + tmux + envs + local ssh_cmd=(ssh -tt "${df_user}@${ssh_host}") + + if [[ "$no_tmux_arg" == "false" ]]; then + # TODO: instead of tmux,maybe use "flow" in order to attach to dev container too + ssh_cmd+=("tmux" "new-session" "-As" "$session_arg" + "-e" "DF_NAMESPACE=$df_namespace" + "-e" "DF_PLATFORM=$df_platform") + fi + + # Run or dryrun? + if [[ "$dry_run_arg" == "true" ]]; then + echo "Dry run command:" + printf '%q ' "${ssh_cmd[@]}" + echo + exit 0 + fi + + exec "${ssh_cmd[@]}" +} + +# shellcheck disable=SC2154,SC2155 +cmd_sync_check() { + local base_dir="$HOME/projects" + local -a needs_action=() + local -a not_git=() + + for repo in "$base_dir"/*; do + [ -d "$repo" ] || continue + local git_dir="$repo/.git" + + if [ ! -d "$git_dir" ]; then + not_git+=("$(basename "$repo")") + continue + fi + + echo "=== $(basename "$repo") ===" + cd "$repo" || continue + + local action_required=0 + + git fetch --all --quiet || true + local branch + branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "HEAD") + + # --- Uncommitted or untracked changes --- + if ! git diff --quiet || ! git diff --cached --quiet; then + echo "Uncommitted changes:" + git status --short + action_required=1 + elif [ -n "$(git ls-files --others --exclude-standard)" ]; then + echo "Untracked files:" + git ls-files --others --exclude-standard + action_required=1 + else + echo "No uncommitted or untracked changes." + fi + + # --- Unpushed commits on current branch --- + if git rev-parse --abbrev-ref "${branch}@{u}" >/dev/null 2>&1; then + local unpushed + unpushed=$(git rev-list --oneline "${branch}@{u}..${branch}") + if [ -n "$unpushed" ]; then + echo "Unpushed commits on ${branch}:" + echo "$unpushed" + action_required=1 + else + echo "No unpushed commits on ${branch}." + fi + else + echo "No upstream set for ${branch}." + action_required=1 + fi + + # --- Unpushed branches --- + local unpushed_branches=() + while IFS= read -r b; do + if git rev-parse --abbrev-ref "${b}@{u}" >/dev/null 2>&1; then + local ahead + ahead=$(git rev-list --count "${b}@{u}..${b}") + if [ "$ahead" -gt 0 ]; then + unpushed_branches+=("$b ($ahead ahead)") + fi + else + unpushed_branches+=("$b (no upstream)") + fi + done < <(git for-each-ref --format='%(refname:short)' refs/heads) + + if [ "${#unpushed_branches[@]}" -gt 0 ]; then + echo "Unpushed branches:" + printf ' %s\n' "${unpushed_branches[@]}" + action_required=1 + else + echo "No unpushed branches." + fi + + echo + ((action_required)) && needs_action+=("$(basename "$repo")") + done + + echo "=== SUMMARY ===" + if [ "${#needs_action[@]}" -gt 0 ]; then + echo "Projects needing action:" + printf ' %s\n' "${needs_action[@]}" | sort -u + else + echo "All repositories clean and synced." + fi + + if [ "${#not_git[@]}" -gt 0 ]; then + echo + echo "Directories without Git repositories:" + printf ' %s\n' "${not_git[@]}" | sort -u + fi +} + +# shellcheck disable=SC2154,SC2155 +cmd_create() { + # VARS: name_arg, image_arg, project_arg + + # Check if container name already exists + local cname="$(get_cname)" + if docker_container_exists "$cname"; then + printf -v msg 'Container already exists: "%s" (from name "%s")' "$cname" "$name_arg" + fail "$msg" + fi + + # Check if project path is valid + local project_path + project_path="$(resolve_path "$project_arg")" + if [[ ! -d "$project_path" ]]; then + fail "Invalid project path: $project_path" + fi + + # Check image + IFS=' ' read -r image_ref _ _ _ <<<"$(parse_image_ref "$image_arg")" + if ! docker_image_present "$image_ref"; then + printf -v msg 'Image not found locally.\nTry:\n\t- docker pull %s' "$image_ref" + fail "$msg" + fi + + # Run (= create and start container) + cmd=( + docker run -d + --name "$cname" + --label dev=true + --label "dev.name=$name_arg" + --label "dev.project_path=$project_path" + --label "dev.image_ref=$image_ref" + --network host + --init # run tini as PID 1 to handle signals & reap zombies for cleaner container shutdown + -v "$project_path:/workspace" + -v /var/run/docker.sock:/var/run/docker.sock + ) + + [[ -d "$HOME/.ssh" ]] && cmd+=(-v "$HOME/.ssh:$CONTAINER_HOME/.ssh:ro") + [[ -f "$HOME/.npmrc" ]] && cmd+=(-v "$HOME/.npmrc:$CONTAINER_HOME/.npmrc:ro") + [[ -d "$HOME/.npm" ]] && cmd+=(-v "$HOME/.npm:$CONTAINER_HOME/.npm") + + docker_gid="$(getent group docker | cut -d: -f3 || true)" + [[ -n "$docker_gid" ]] && cmd+=(--group-add "$docker_gid") + + cmd+=("$image_ref" sleep infinity) + "${cmd[@]}" + + printf "Created and started container: %s" "$cname" +} + +# shellcheck disable=SC2154,SC2155 +cmd_connect() { + # VARS: name_arg + + local cname="$(get_cname)" + if ! docker_container_exists "$cname"; then + fail "Container does not exist: ${cname}. Run: dev create ..." + fi + + if ! docker_container_running "$cname"; then + docker start "$cname" >/dev/null + fi + + if ! command -v tmux >/dev/null 2>&1; then + echo "tmux not found; falling back to direct exec" + exec "$0" exec "$cname" + fi + + local image_ref + image_ref="$(docker container inspect "$cname" --format '{{ .Config.Image }}')" + IFS=' ' read -r _image_ref _ _ image_label <<<"$(parse_image_ref "$image_ref")" + + if ! tmux has-session -t "$cname" 2>/dev/null; then + tmux new-session -ds "$cname" \ + -e "DF_IMAGE=$image_label" \ + -e "DF_NAMESPACE=$DF_NAMESPACE" \ + -e "DF_PLATFORM=$DF_PLATFORM" \ + "$0 exec \"$name_arg\"" + tmux set-option -t "$cname" default-command "$0 exec \"$name_arg\"" + fi + + if [[ -n "${TMUX-}" ]]; then + tmux switch-client -t "$cname" + else + tmux attach -t "$cname" + fi +} + +# shellcheck disable=SC2154,SC2155 +cmd_exec() { + # VARS: name_arg, cmd_arg + + local cname="$(get_cname)" + if ! docker_container_running "$cname"; then + fail "Container $cname not running" + fi + + if [[ -n "$cmd_arg" ]]; then + if [[ -t 0 ]]; then + docker exec -it "$cname" "${cmd_arg}" + else + docker exec "$cname" "${cmd_arg}" + fi + return + fi + + # No command provided -> open a shell + docker exec --detach-keys "ctrl-q,ctrl-p" -it "$cname" zsh -l || + docker exec --detach-keys "ctrl-q,ctrl-p" -it "$cname" bash -l || + docker exec --detach-keys "ctrl-q,ctrl-p" -it "$cname" sh +} + +shorten_project_path() { + local project=$1 + local home=${HOME%/} + local projdir=${PROJECT_DIR%/} + + # Case 1: under PROJECT_DIR + if [[ -n ${projdir} && $project == "$projdir"/* ]]; then + # shellcheck disable=SC2088 + project="~/$PROJECT_ABBR${project#"$projdir"}" + + # Case 2: equals HOME + elif [[ $project == "$home" ]]; then + project="~" + + # Case 3: under HOME (but not PROJECT_DIR) + elif [[ $project == "$home"/* ]]; then + project="~${project#"$home"}" + fi + + printf '%s\n' "$project" +} + +# shellcheck disable=SC2154,SC2155 +cmd_list() { + # VARS: + + { + echo "NAME|IMAGE|PROJECT|STATUS" + docker ps -a --filter "label=dev=true" \ + --format '{{.Label "dev.name"}}|{{.Image}}|{{.Label "dev.project_path"}}|{{.Status}}' + } | while IFS='|' read -r fname image project status; do + # Shorten registry prefix + image="${image/$REGISTRY\//$REGISTRY_ABBR/}" + + # Shorten project path + project="$(shorten_project_path "$project")" + + echo "$fname|$image|$project|$status" + done | column -t -s '|' +} + +tmux_fallback_to_default_if_in_session() { + # If inside tmux and current session matches the given one, + # switch to or create 'default' before proceeding. + local target_session="$1" + + [[ -z "${TMUX-}" ]] && return 0 # not in tmux, nothing to do + + local current_session + current_session="$(tmux display-message -p '#S')" + + if [[ "$current_session" == "$target_session" ]]; then + if ! tmux has-session -t default 2>/dev/null; then + tmux new-session -ds default + fi + tmux switch-client -t default + fi +} + +# shellcheck disable=SC2154,SC2155 +cmd_stop() { + # VARS: kill_arg name_arg + local cname + cname="$(get_cname)" + docker_container_exists "$cname" || fail "Container $cname does not exist" + + if [[ "$kill_arg" == "true" ]]; then + echo "Killing container $cname..." + docker kill "$cname" + else + echo "Stopping container $cname..." + docker stop "$cname" + fi + + tmux_fallback_to_default_if_in_session "$cname" +} + +# shellcheck disable=SC2154,SC2155 +cmd_remove() { + # VARS: force_arg name_arg + local cname + cname="$(get_cname)" + docker_container_exists "$cname" || fail "Container $cname does not exist" + + if [[ "$force_arg" == "true" ]]; then + echo "Removing container $cname (force)..." + docker rm -f "$cname" + else + echo "Removing container $cname..." + docker rm "$cname" + fi + + tmux_fallback_to_default_if_in_session "$cname" +} + +# shellcheck disable=SC2154,SC2155 +cmd_respawn() { + # VARS: name_arg + local cname + cname="$(get_cname)" + panes=$(tmux list-panes -t "$cname" -s -F "#{session_name}:#{window_index}.#{pane_index}") + + for pane in $panes; do + echo "Respawning $pane..." + tmux respawn-pane -t "$pane" + done +} + +# shellcheck disable=SC2154,SC2155 +cmd_test() { + # VARS: name_arg + + echo "Script dev is working fine!" + if [[ -n "$name_arg" ]]; then + get_cname + fi + echo +} + +barg_run SPEC[@] "$@" diff --git a/_shared/bin/.bin/ssh-forward b/_shared/bin/.bin/ssh-forward new file mode 100755 index 0000000..b9db6f0 --- /dev/null +++ b/_shared/bin/.bin/ssh-forward @@ -0,0 +1,29 @@ +#!/bin/bash + +# Function to display usage information +usage() { + echo "Usage: $0 [port2] [port3] ..." + exit 1 +} + +# Ensure at least two arguments are provided: host and one port +if [ "$#" -lt 2 ]; then + usage +fi + +# Extract the host from the first argument +HOST="$1" +shift # Shift the arguments so that $@ contains the remaining ports + +# Initialize the PORTS variable +PORTS="" + +# Iterate over the remaining arguments, which are the ports +for port in "$@"; do + PORTS="$PORTS -L ${port}:localhost:${port}" +done + +# Construct and run the SSH command +SSH_CMD="ssh -N -T -o ExitOnForwardFailure=yes $HOST $PORTS" +echo "Running: $SSH_CMD" +$SSH_CMD diff --git a/_shared/bin/.bin/sync-theme b/_shared/bin/.bin/sync-theme new file mode 100755 index 0000000..5e154ef --- /dev/null +++ b/_shared/bin/.bin/sync-theme @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set -euo pipefail + +declare -A THEME=( + ["ghostty.url"]="https://raw.githubusercontent.com/triimdev/invero.nvim/refs/heads/main/extras/ghostty/invero_day" + ["ghostty.dir"]="$HOME/.config/ghostty/themes" + ["ghostty.name"]="Invero Day" + + ["wezterm.url"]="https://raw.githubusercontent.com/triimdev/invero.nvim/refs/heads/main/extras/wezterm/invero_day.toml" + ["wezterm.dir"]="$HOME/.config/wezterm/colors" + ["wezterm.name"]="Invero Day.toml" +) + +theme="${1:-}" + +if [[ -z "$theme" ]]; then + echo "Usage: $0 " + echo "Available themes: $(printf '%s\n' "${!THEME[@]}" | cut -d. -f1 | sort -u | tr '\n' ' ')" + exit 1 +fi + +if [[ -z "${THEME[$theme.url]+x}" ]]; then + echo "Unknown theme '$theme'. Available: $(printf '%s\n' "${!THEME[@]}" | cut -d. -f1 | sort -u | tr '\n' ' ')" + exit 1 +fi + +url="${THEME[$theme.url]}" +dir="${THEME[$theme.dir]}" +name="${THEME[$theme.name]}" +path="${dir}/${name}" + +mkdir -p "$dir" + +if curl -fsSL -o "$path" "$url"; then + echo "Theme downloaded to $path" +else + echo "Failed to download theme for '$theme'." + exit 1 +fi diff --git a/_shared/bin/.bin/test-true-color b/_shared/bin/.bin/test-true-color new file mode 100755 index 0000000..8b46ef9 --- /dev/null +++ b/_shared/bin/.bin/test-true-color @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +echo +awk 'BEGIN{ + s="/\\/\\/\\/\\/\\"; s=s s s s s s s s; + for (colnum = 0; colnum<77; colnum++) { + r = 255-(colnum*255/76); + g = (colnum*510/76); + b = (colnum*255/76); + if (g>255) g = 510-g; + printf "\033[48;2;%d;%d;%dm", r,g,b; + printf "\033[38;2;%d;%d;%dm", 255-r,255-g,255-b; + printf "%s\033[0m", substr(s,colnum+1,1); + } + printf "\n"; +}' + +# --- Environment diagnostics ------------------------------------------------- +echo +echo "──────────────────────────────" +echo " Environment and Tmux Details " +echo "──────────────────────────────" +echo "TERM: ${TERM}" +echo "COLORTERM: ${COLORTERM:-undefined}" +echo + +if command -v tmux >/dev/null && tmux info &>/dev/null; then + echo "Tmux RGB/Tc capabilities:" + tmux info | grep -E "RGB|Tc" || echo "(none found)" + echo + echo "Tmux server terminal options:" + tmux show-options -s | grep terminal || echo "(none found)" +else + echo "Tmux not running or unavailable." +fi + +# --- Underline capability test ----------------------------------------------- +echo +echo "Underline styles test:" +printf '\x1b[58:2::255:0:0m' # red underline color +printf '\x1b[4:1msingle ' # single underline +printf '\x1b[4:2mdouble ' # double underline +printf '\x1b[4:3mcurly ' # curly underline +printf '\x1b[4:4mdotted ' # dotted underline +printf '\x1b[4:5mdashed ' # dashed underline +printf '\x1b[0m\n' + +echo diff --git a/_shared/dnsmasq/.user_hosts b/_shared/dnsmasq/.user_hosts new file mode 100644 index 0000000..79950bf --- /dev/null +++ b/_shared/dnsmasq/.user_hosts @@ -0,0 +1,3 @@ +192.168.64.2 personal.utm.local +192.168.64.3 university.utm.local +192.168.50.2 personal.workstation.lan diff --git a/_shared/dnsmasq/_root/opt/homebrew/etc/dnsmasq.conf b/_shared/dnsmasq/_root/opt/homebrew/etc/dnsmasq.conf new file mode 100644 index 0000000..6dae1a9 --- /dev/null +++ b/_shared/dnsmasq/_root/opt/homebrew/etc/dnsmasq.conf @@ -0,0 +1,12 @@ +# flow-managed dnsmasq configuration + +domain-needed +bogus-priv + +# Keep loopback for local resolver usage. +listen-address=127.0.0.1 + +# Local VM and workstation aliases. +address=/personal.utm.local/192.168.64.2 +address=/university.utm.local/192.168.64.3 +address=/personal.workstation.lan/192.168.50.2 diff --git a/_shared/flow/.config/flow/config.yaml b/_shared/flow/.config/flow/config.yaml new file mode 100644 index 0000000..cefad9f --- /dev/null +++ b/_shared/flow/.config/flow/config.yaml @@ -0,0 +1,16 @@ +repository: + dotfiles-url: git@gitea.tomastm.com:tomas.mirchev/dotfiles.git + dotfiles-branch: main + +paths: + projects-dir: ~/projects + +defaults: + container-registry: registry.tomastm.com + container-tag: latest + tmux-session: default + +targets: + personal: orb personal.orb + # Add more SSH targets as needed + # Format: :
[ssh-key-path] diff --git a/_shared/flow/.config/flow/packages.yaml b/_shared/flow/.config/flow/packages.yaml new file mode 100644 index 0000000..7335349 --- /dev/null +++ b/_shared/flow/.config/flow/packages.yaml @@ -0,0 +1,136 @@ +packages: + - name: git + type: pkg + sources: + apt: git + dnf: git + brew: git + + - name: tmux + type: pkg + sources: + apt: tmux + dnf: tmux + brew: tmux + + - name: zsh + type: pkg + sources: + apt: zsh + dnf: zsh + brew: zsh + + - name: dnsmasq + type: pkg + sources: + brew: dnsmasq + + - name: brave + type: cask + sources: + brew: brave-browser + + - name: bruno + type: cask + sources: + brew: bruno + + - name: dbeaver + type: cask + sources: + brew: dbeaver-community + + - name: discord + type: cask + sources: + brew: discord + + - name: firefox + type: cask + sources: + brew: firefox + + - name: font-maple-mono + type: cask + sources: + brew: font-maple-mono + + - name: font-maple-mono-nf + type: cask + sources: + brew: font-maple-mono-nf + + - name: ghostty + type: cask + sources: + brew: ghostty + + - name: chrome + type: cask + sources: + brew: google-chrome + + - name: karabiner + type: cask + sources: + brew: karabiner-elements + + - name: linearmouse + type: cask + sources: + brew: linearmouse + + - name: macfuse + type: cask + sources: + brew: macfuse + + - name: orbstack + type: cask + sources: + brew: orbstack + + - name: proton-drive + type: cask + sources: + brew: proton-drive + + - name: protonvpn + type: cask + sources: + brew: protonvpn + + - name: rectangle + type: cask + sources: + brew: rectangle + + - name: slack + type: cask + sources: + brew: slack + + - name: sol + type: cask + sources: + brew: sol + + - name: spotify + type: cask + sources: + brew: spotify + + - name: sublime-text + type: cask + sources: + brew: sublime-text + + - name: utm + type: cask + sources: + brew: utm@beta + + - name: zoom + type: cask + sources: + brew: zoom diff --git a/_shared/flow/.config/flow/profiles.yaml b/_shared/flow/.config/flow/profiles.yaml new file mode 100644 index 0000000..3b1f511 --- /dev/null +++ b/_shared/flow/.config/flow/profiles.yaml @@ -0,0 +1,78 @@ +profiles: + macos: + os: macos + package-manager: brew + shell: zsh + requires: [HOSTNAME, USER_EMAIL] + hostname: "{{ env.HOSTNAME }}" + packages: + - git + - tmux + - zsh + - dnsmasq + - cask/brave + - cask/bruno + - cask/dbeaver + - cask/discord + - cask/firefox + - cask/font-maple-mono + - cask/font-maple-mono-nf + - cask/ghostty + - cask/chrome + - cask/karabiner + - cask/linearmouse + - cask/macfuse + - cask/orbstack + - cask/proton-drive + - cask/protonvpn + - cask/rectangle + - cask/slack + - cask/sol + - cask/spotify + - cask/sublime-text + - cask/utm + - cask/zoom + - name: setup + post-install: | + chmod +x ~/.local/share/setup/* 2>/dev/null || true + if [ -f ~/.local/share/setup/macos-defaults ]; then + ~/.local/share/setup/macos-defaults + fi + - name: dnsmasq-service + allow_sudo: true + post-install: | + sudo brew services restart dnsmasq || sudo brew services start dnsmasq + ssh-keygen: + - type: ed25519 + filename: id_ed25519 + comment: "{{ env.USER_EMAIL }}" + - type: ed25519 + filename: id_ed25519_git + comment: "{{ env.USER_EMAIL }}" + runcmd: + - mkdir -p ~/projects + - mkdir -p ~/.bin + - git config --global user.email "{{ env.USER_EMAIL }}" + - git config --global user.name "Tomas Mirchev" + - chmod +x ~/.bin/* 2>/dev/null || true + post-link: | + echo "macOS dotfiles linked." + echo "Import Rectangle config from ~/.config/rectangle/RectangleConfig.json" + + linux: + os: linux + requires: [USER_EMAIL] + shell: zsh + packages: + - name: window-tagger + post-install: | + echo "Window Tagger config linked to ~/.local/share/kwin/scripts/window-tagger" + ssh-keygen: + - type: ed25519 + filename: id_ed25519 + comment: "{{ env.USER_EMAIL }}" + runcmd: + - mkdir -p ~/projects + - mkdir -p ~/.bin + - git config --global user.email "{{ env.USER_EMAIL }}" + - git config --global user.name "Tomas Mirchev" diff --git a/_shared/git/.gitconfig b/_shared/git/.gitconfig new file mode 100644 index 0000000..8b31831 --- /dev/null +++ b/_shared/git/.gitconfig @@ -0,0 +1,24 @@ +[user] + name = Tomas Mirchev + email = contact@tomastm.com +[core] + editor = nvim + excludesfile = ~/.gitignore +[init] + defaultBranch = main +[pull] + rebase = true +[push] + default = current + autoSetupRemote = true +[remote] + pushDefault = origin +[alias] + amend = commit --amend --no-edit + rename = branch -m + st = status + unstage = reset HEAD + last = log -1 HEAD + tags = tag -l + undo = reset --mixed HEAD~1 + diff --git a/_shared/gitignore/.gitignore b/_shared/gitignore/.gitignore new file mode 100644 index 0000000..ffe8af7 --- /dev/null +++ b/_shared/gitignore/.gitignore @@ -0,0 +1,21 @@ +.DS_Store +*.swp +*.swo +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +.env +.env.* +build/ +dist/ +build/ +tmp/ +temp/ +logs/ +*.log +.cache/ +coverage/ +.devflow/ +.dev-flow/ diff --git a/_shared/htop/.config/htop/htoprc b/_shared/htop/.config/htop/htoprc new file mode 100644 index 0000000..685c07e --- /dev/null +++ b/_shared/htop/.config/htop/htoprc @@ -0,0 +1,63 @@ +# Beware! This file is rewritten by htop when settings are changed in the interface. +# The parser is also very primitive, and not human-friendly. +htop_version=3.2.2 +config_reader_min_version=3 +fields=0 48 17 18 38 39 40 2 46 47 49 1 +hide_kernel_threads=1 +hide_userland_threads=1 +hide_running_in_container=0 +shadow_other_users=0 +show_thread_names=0 +show_program_path=1 +highlight_base_name=0 +highlight_deleted_exe=1 +shadow_distribution_path_prefix=0 +highlight_megabytes=1 +highlight_threads=1 +highlight_changes=0 +highlight_changes_delay_secs=5 +find_comm_in_cmdline=1 +strip_exe_from_cmdline=1 +show_merged_command=0 +header_margin=1 +screen_tabs=1 +detailed_cpu_time=0 +cpu_count_from_one=0 +show_cpu_usage=1 +show_cpu_frequency=0 +show_cpu_temperature=0 +degree_fahrenheit=0 +update_process_names=0 +account_guest_in_cpu_meter=0 +color_scheme=0 +enable_mouse=1 +delay=15 +hide_function_bar=0 +header_layout=two_50_50 +column_meters_0=AllCPUs Memory Swap +column_meter_modes_0=1 1 1 +column_meters_1=Tasks LoadAverage Uptime +column_meter_modes_1=2 2 2 +tree_view=0 +sort_key=47 +tree_sort_key=0 +sort_direction=-1 +tree_sort_direction=1 +tree_view_always_by_pid=0 +all_branches_collapsed=0 +screen:Main=PID USER PRIORITY NICE M_VIRT M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME Command +.sort_key=PERCENT_MEM +.tree_sort_key=PID +.tree_view=0 +.tree_view_always_by_pid=0 +.sort_direction=-1 +.tree_sort_direction=1 +.all_branches_collapsed=0 +screen:I/O=PID USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE PERCENT_SWAP_DELAY PERCENT_IO_DELAY Command +.sort_key=IO_RATE +.tree_sort_key=PID +.tree_view=0 +.tree_view_always_by_pid=0 +.sort_direction=-1 +.tree_sort_direction=1 +.all_branches_collapsed=0 diff --git a/_shared/nvim/_module.yaml b/_shared/nvim/_module.yaml new file mode 100644 index 0000000..ce17a49 --- /dev/null +++ b/_shared/nvim/_module.yaml @@ -0,0 +1,3 @@ +source: git@gitea.tomastm.com:tomas.mirchev/nvim-config.git +ref: + branch: main diff --git a/_shared/tmux/.tmux.conf b/_shared/tmux/.tmux.conf new file mode 100644 index 0000000..0757671 --- /dev/null +++ b/_shared/tmux/.tmux.conf @@ -0,0 +1,76 @@ +##### Prefix ##### +unbind C-b +set -g prefix C-Space +bind C-Space send-prefix + +##### General ##### +# set -s default-terminal "tmux-256color" +# set -sa terminal-overrides "$term:rgb" + +set -s escape-time 10 +set -s focus-events on +set -s set-clipboard on + +# set -g default-command "${SHELL}" +set -g base-index 1 # window index +set -g renumber-windows on # window index +set -g history-limit 10000 +set -g repeat-time 0 +set -g mouse on +set -g set-titles on +set -g set-titles-string "#S" + +# set -g remain-on-exit on + +set -gw pane-base-index 1 # pane index + +##### Appearance ##### +set -g status-style fg=black,bg=default +set -g window-status-current-style fg=blue,bold +set -g message-style fg=blue,bg=default +set -g status-left "#[fg=blue,bold][#{s/^dev-//:#{session_name}}] " +set -g status-right " #{?DF_IMAGE,#{DF_IMAGE} | ,}#{?DF_NAMESPACE,#{DF_NAMESPACE},#H}@#{?DF_PLATFORM,#{DF_PLATFORM},local}" +set -g status-left-length 50 +set -g status-right-length 50 +set -g pane-active-border-style fg=blue + +##### Vim-like ##### +set -gw mode-keys vi +bind -T copy-mode-vi v send-keys -X begin-selection +bind -T copy-mode-vi WheelUpPane send -N1 -X scroll-up +bind -T copy-mode-vi WheelDownPane send -N1 -X scroll-down +bind -r h select-pane -L +bind -r j select-pane -D +bind -r k select-pane -U +bind -r l select-pane -R + +# Resize Opt/Alt-hjkl +bind -n M-h resize-pane -L 5 +bind -n M-j resize-pane -D 5 +bind -n M-k resize-pane -U 5 +bind -n M-l resize-pane -R 5 + +# Last window instead of session +bind ';' last-window + +unbind '"' +unbind "%" +unbind s +unbind c +unbind n +unbind x + +bind s split-window -v -c "#{pane_current_path}" +bind v split-window -h -c "#{pane_current_path}" +bind o choose-session +bind n new-window +bind c confirm-before -p "kill-pane \#P? (y/n)" kill-pane + +##### Misc ##### +bind r source-file ~/.tmux.conf \; display "Reloaded!" + +unbind d +bind e detach + +bind d command-prompt -I "flow " 'run-shell "~/.bin/dev-tmux-wrapper %1 --from #{session_name}"' + diff --git a/_shared/zsh/.zshrc b/_shared/zsh/.zshrc new file mode 100644 index 0000000..5e41e79 --- /dev/null +++ b/_shared/zsh/.zshrc @@ -0,0 +1,82 @@ +export PATH="$PATH:$HOME/.bin:$HOME/.local/bin:$HOME/bin" +export LANG=en_US.UTF-8 +export LC_CTYPE=en_US.UTF-8 +export LC_COLLATE=C + +# eval "$(dircolors)"; echo "$LS_COLORS" +export LS_COLORS='rs=0:di=01;34:ln=01;33:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=00:tw=30;42:ow=34;42:st=37;44:ex=01;32' + +HISTFILE=$HOME/.zsh_history +HISTSIZE=10000 +SAVEHIST=10000 + +setopt auto_cd interactive_comments prompt_subst share_history +setopt append_history hist_ignore_dups hist_ignore_all_dups hist_reduce_blanks + +autoload -Uz compinit +zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' # Case-insensitive +zstyle ':completion:*' use-cache on +zstyle ':completion:*' cache-path ~/.zsh/cache +compinit + +git_prompt_info() { + if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + local branch=$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD) + echo " %F{green}($branch)%f" + fi +} +abbrev_path() { + local pwd="${PWD/#$HOME/~}" + local parts=("${(@s:/:)pwd}") + local len=${#parts} + + if (( len <= 1 )); then + echo "$pwd" + return + fi + + local result="" + for (( i=1; i/dev/null 2>&1; then + alias vim='nvim' +fi + +case "$OSTYPE" in + linux*) alias ls='ls --color=auto --group-directories-first' ;; + darwin*) alias ls='ls --color=auto' ;; +esac + +h() { + history 0 | grep -iE --color=always "$@" | tail -20 +} + +alias ll='ls -lF' +alias lla='ll -a' +alias ld='ls -ld */' + +alias ga='git add' +alias gcm='git commit -m' +alias gp='git push' +alias gst='git status' +alias gd='git diff --patience --color-moved=dimmed-zebra --word-diff=plain --function-context --ignore-space-change -U3' +alias glg='git log --oneline --graph --decorate --all' + +alias k='kubectl' diff --git a/linux/kwin/.local/share/kwin/scripts/window-tagger/contents/code/main.js b/linux/kwin/.local/share/kwin/scripts/window-tagger/contents/code/main.js new file mode 100644 index 0000000..3fd6c19 --- /dev/null +++ b/linux/kwin/.local/share/kwin/scripts/window-tagger/contents/code/main.js @@ -0,0 +1,68 @@ +const version = "v11" +//console.info("WINDOW TAGGER STARTED - " + version) + +const windows = Array.from({length: 9}, () => null) + +for (let i = 0; i < 9; i++) { + registerShortcut( + `TagWindow${i+1}`, + `Tag current window to tag ${i+1}`, + `Meta+Shift+${i+1}`, + function() { + try { + //console.info(`Trying to tag at ${i+1}`) + if (!workspace.activeWindow) { + //console.info("No active window to tag") + return + } + + //console.info(`Tag ${i+1}: ${workspace.activeWindow.caption}`) + for (let j = 0; j < 9; j++) { + if (windows[j] === workspace.activeWindow) { + windows[j] = null; + } + } + windows[i] = workspace.activeWindow + } catch (e) { + console.info(e) + } + } + ); + + registerShortcut( + `FocusWindow${i}`, + `Focus Window at tag ${i+1}`, + `Meta+${i+1}`, + function() { + try { + //console.info(`Total: ${windows.filter(w => w !== null).length}`) + windows.forEach(w => { + if (w) { + //console.info(`- ${w.caption}`) + } + }) + if (!windows[i]) { + //console.info("Tag is empty") + return + } + + if (windows[i] === workspace.activeWindow) { + windows[i].minimized = true + //console.info("Focusing already focused window") + return + } + + try { + workspace.activeWindow = windows[i] + } catch (error ) { + // console.info(windows[i].valid, windows[i].deleted) + // console.info("Error: ", error) + windows[i] = null + } + } catch (e) { + // console.info(e) + } + } + ); +} + diff --git a/linux/kwin/.local/share/kwin/scripts/window-tagger/metadata.json b/linux/kwin/.local/share/kwin/scripts/window-tagger/metadata.json new file mode 100644 index 0000000..fbb3261 --- /dev/null +++ b/linux/kwin/.local/share/kwin/scripts/window-tagger/metadata.json @@ -0,0 +1,21 @@ +{ + "KPlugin": { + "Name": "Window Tagger", + "Description": "Tag windows with numbers and quickly switch between them", + "Icon": "preferences-system-windows", + + "Authors": [ + { + "Email": "username@gmail.com", + "Name": "Firstname Lastname" + } + ], + "Id": "window-tagger", + "Version": "1.0", + "License": "GPLv3", + "Website": "https://github.com/username/myscript" + }, + "X-Plasma-API": "javascript", + "X-Plasma-MainScript": "code/main.js", + "KPackageStructure": "KWin/Script" +} diff --git a/linux/kwin/.local/share/kwin/scripts/window-tagger/path.txt b/linux/kwin/.local/share/kwin/scripts/window-tagger/path.txt new file mode 100644 index 0000000..c35c214 --- /dev/null +++ b/linux/kwin/.local/share/kwin/scripts/window-tagger/path.txt @@ -0,0 +1 @@ +~/.local/share/kwin/scripts diff --git a/macos/alacritty/.config/alacritty/alacritty.toml b/macos/alacritty/.config/alacritty/alacritty.toml new file mode 100644 index 0000000..00b21fb --- /dev/null +++ b/macos/alacritty/.config/alacritty/alacritty.toml @@ -0,0 +1,78 @@ +[env] +# TERM = "xterm-256color" + +[font] +size = 14 +normal = { family = "SF Mono", style = "Regular" } +bold = { family = "SF Mono", style = "Bold" } +italic = { family = "SF Mono", style = "Regular Italic" } +bold_italic = { family = "SF Mono", style = "Bold Italic" } + +# normal = { family = "Maple Mono", style = "Regular" } +# bold = { family = "Maple Mono", style = "Bold" } +# italic = { family = "Maple Mono", style = "Italic" } +# bold_italic = { family = "Maple Mono", style = "Bold Italic" } +# offset = { x = -1, y = 0 } + +[window] +padding = { x = 2, y = 0 } +dynamic_padding = true +# resize_increments = true + +[keyboard] +bindings = [ + # Create new window + { action = "SpawnNewInstance", key = "N", mods = "Command" }, + # Jump back one word + { key = "Left", mods = "Alt", chars = "\u001bb" }, + # Jump forward one word + { key = "Right", mods = "Alt", chars = "\u001bf" }, + # Move to start of line + { key = "Left", mods = "Command", chars = "\u0001" }, + # Move to end of line + { key = "Right", mods = "Command", chars = "\u0005" }, + # Delete backwards + { key = "Back", mods = "Alt", chars = "\u001B\u007F" }, # word + { key = "Back", mods = "Command", chars = "\u0015" }, # line + # Delete forwards + { key = "Delete", mods = "Alt", chars = "\u001Bd" }, # word + { key = "Delete", mods = "Command", chars = "\u000B" } # line +] + +[scrolling] +multiplier = 1 + +[general] +live_config_reload = true + +[colors.primary] +background = "#eeeeee" +foreground = "#444444" + +[colors.cursor] +text = "#eeeeee" +cursor = "#005fff" + +[colors.selection] +text = "#434343" +background = "#e0e0e0" + +[colors.normal] +black = "#000000" +red = "#aa3731" +green = "#448c27" +yellow = "#cb9000" +blue = "#325cc0" +magenta = "#7a3e9d" +cyan = "#0083b2" +white = "#bbbbbb" + +[colors.bright] +black = "#000000" +red = "#aa3731" +green = "#448c27" +yellow = "#cb9000" +blue = "#325cc0" +magenta = "#7a3e9d" +cyan = "#0083b2" +white = "#bbbbbb" diff --git a/macos/alacritty/.config/alacritty/themes/alabaster.toml b/macos/alacritty/.config/alacritty/themes/alabaster.toml new file mode 100644 index 0000000..da82704 --- /dev/null +++ b/macos/alacritty/.config/alacritty/themes/alabaster.toml @@ -0,0 +1,27 @@ +[colors.primary] +background = '#F7F7F7' +foreground = '#434343' + +[colors.cursor] +text = '#F7F7F7' +cursor = '#434343' + +[colors.normal] +black = '#000000' +red = '#AA3731' +green = '#448C27' +yellow = '#CB9000' +blue = '#325CC0' +magenta = '#7A3E9D' +cyan = '#0083B2' +white = '#BBBBBB' + +[colors.bright] +black = '#777777' +red = '#F05050' +green = '#60CB00' +yellow = '#FFBC5D' +blue = '#007ACC' +magenta = '#E64CE6' +cyan = '#00AACB' +white = '#FFFFFF' diff --git a/macos/borders/.config/borders/bordersrc b/macos/borders/.config/borders/bordersrc new file mode 100755 index 0000000..065fad4 --- /dev/null +++ b/macos/borders/.config/borders/bordersrc @@ -0,0 +1,13 @@ +#!/bin/bash + +options=( + order=above + width=2.0 + hidpi=on + active_color=0xff2b2b2b + inactive_color=0xff2b2b2b + # inactive_color=0x00000000 + whitelist="wezterm-gui" +) + +borders "${options[@]}" diff --git a/macos/ghostty/.config/ghostty/Ghostty.icns b/macos/ghostty/.config/ghostty/Ghostty.icns new file mode 100644 index 0000000..55bdfd5 Binary files /dev/null and b/macos/ghostty/.config/ghostty/Ghostty.icns differ diff --git a/macos/ghostty/.config/ghostty/config b/macos/ghostty/.config/ghostty/config new file mode 100644 index 0000000..cb54777 --- /dev/null +++ b/macos/ghostty/.config/ghostty/config @@ -0,0 +1,33 @@ +#term = xterm-256color +theme = Invero Day + +# Font +font-family = "Maple Mono" +font-size = 13 +font-thicken = true +font-thicken-strength = 120 +font-feature = -liga, -dlig, -calt + +adjust-underline-thickness = 1 +adjust-strikethrough-thickness = 1 +adjust-overline-thickness = 1 +adjust-box-thickness = 1 +adjust-cell-width = -7% +adjust-cell-height = -2 + +# Cursor +cursor-style = block +cursor-style-blink = false +mouse-hide-while-typing = true +shell-integration-features = ssh-terminfo,ssh-env,no-cursor + +# Window +window-height = 80 +window-width = 128 +window-padding-x = 4 +window-padding-y = 0 +window-padding-color = extend +macos-titlebar-style = native +macos-icon = custom + +window-inherit-working-directory = false diff --git a/macos/ghostty/.config/ghostty/themes/Invero Day b/macos/ghostty/.config/ghostty/themes/Invero Day new file mode 100644 index 0000000..3c2c199 --- /dev/null +++ b/macos/ghostty/.config/ghostty/themes/Invero Day @@ -0,0 +1,22 @@ +palette = 0=#444444 +palette = 1=#ff0000 +palette = 2=#00af5f +palette = 3=#d75f00 +palette = 4=#005fff +palette = 5=#5f5f87 +palette = 6=#afd7ff +palette = 7=#eeeeee +palette = 8=#444444 +palette = 9=#ff0000 +palette = 10=#00af5f +palette = 11=#d75f00 +palette = 12=#005fff +palette = 13=#5f5f87 +palette = 14=#afd7ff +palette = 15=#eeeeee + +background = #eeeeee +foreground = #444444 +cursor-color = #005fff +selection-background = #dadada +selection-foreground = #444444 diff --git a/macos/homebrew/.config/homebrew/Brewfile b/macos/homebrew/.config/homebrew/Brewfile new file mode 100644 index 0000000..43c85bc --- /dev/null +++ b/macos/homebrew/.config/homebrew/Brewfile @@ -0,0 +1,25 @@ +brew "bash" +brew "dnsmasq", restart_service: :changed +brew "tmux" +cask "brave-browser" +cask "bruno" +cask "dbeaver-community" +cask "discord" +cask "firefox" +cask "font-maple-mono" +cask "font-maple-mono-nf" +cask "ghostty" +cask "google-chrome" +cask "karabiner-elements" +cask "linearmouse" +cask "macfuse" +cask "orbstack" +cask "proton-drive" +cask "protonvpn" +cask "rectangle" +cask "slack" +cask "sol" +cask "spotify" +cask "sublime-text" +cask "utm@beta" +cask "zoom" diff --git a/macos/karabiner/.config/karabiner/automatic_backups/karabiner_20251105.json b/macos/karabiner/.config/karabiner/automatic_backups/karabiner_20251105.json new file mode 100644 index 0000000..db0f182 --- /dev/null +++ b/macos/karabiner/.config/karabiner/automatic_backups/karabiner_20251105.json @@ -0,0 +1,15 @@ +{ + "profiles": [ + { + "name": "Default profile", + "selected": true, + "simple_modifications": [ + { + "from": { "key_code": "caps_lock" }, + "to": [{ "key_code": "left_control" }] + } + ], + "virtual_hid_keyboard": { "keyboard_type_v2": "iso" } + } + ] +} \ No newline at end of file diff --git a/macos/karabiner/.config/karabiner/karabiner.json b/macos/karabiner/.config/karabiner/karabiner.json new file mode 100644 index 0000000..1ca5d53 --- /dev/null +++ b/macos/karabiner/.config/karabiner/karabiner.json @@ -0,0 +1,34 @@ +{ + "global": { "show_in_menu_bar": false }, + "profiles": [ + { + "devices": [ + { + "identifiers": { + "is_keyboard": true, + "product_id": 50475, + "vendor_id": 1133 + }, + "ignore_vendor_events": true + }, + { + "identifiers": { + "is_keyboard": true, + "product_id": 50504, + "vendor_id": 1133 + }, + "ignore_vendor_events": true + } + ], + "name": "Default profile", + "selected": true, + "simple_modifications": [ + { + "from": { "key_code": "caps_lock" }, + "to": [{ "key_code": "left_control" }] + } + ], + "virtual_hid_keyboard": { "keyboard_type_v2": "iso" } + } + ] +} \ No newline at end of file diff --git a/macos/kitty/.config/kitty/choose_tab.py b/macos/kitty/.config/kitty/choose_tab.py new file mode 100644 index 0000000..76852ba --- /dev/null +++ b/macos/kitty/.config/kitty/choose_tab.py @@ -0,0 +1,54 @@ +# ~/.config/kitty/choose_tab.py +from kitty.boss import get_boss +from kittens.tui.handler import Handler +from kittens.tui.loop import Loop + + +class TabPicker(Handler): + def __init__(self): + super().__init__() + boss = get_boss() + win = boss.active_window + self.osw = win.os_window if win else None + self.tabs = list(self.osw.tabs) if self.osw else [] + self.index = 0 + + def draw(self, screen): + screen.clear() + if not self.tabs: + screen.write_line("No tabs. Esc to exit.") + else: + screen.write_line("Choose a tab (↑/↓ Enter Esc)") + for i, t in enumerate(self.tabs): + mark = "●" if t is self.osw.active_tab else " " + sel = ">" if i == self.index else " " + title = t.title or f"Tab {i+1}" + screen.write_line(f"{sel} {mark} {title}") + screen.refresh() + + def on_key(self, event): + if not self.tabs: + if event.key in ("escape", "enter"): + self.quit_loop() + return + k = event.key + if k in ("up", "k"): + self.index = (self.index - 1) % len(self.tabs) + elif k in ("down", "j"): + self.index = (self.index + 1) % len(self.tabs) + elif k == "enter": + self.osw.set_active_tab(self.tabs[self.index]) + self.quit_loop() + elif k == "escape": + self.quit_loop() + self.refresh() + + +def main(args): + # Correct signature for older Kitty: pass the class name and a title string + Loop(TabPicker, "choose_tab").run() + + +def handle_result(args, answer, target_window_id, boss): + pass + diff --git a/macos/kitty/.config/kitty/invero.conf b/macos/kitty/.config/kitty/invero.conf new file mode 100644 index 0000000..964ef00 --- /dev/null +++ b/macos/kitty/.config/kitty/invero.conf @@ -0,0 +1,35 @@ +# vim:ft=kitty + +background #eeeeee +foreground #444444 +cursor #005fff +cursor_text_color #eeeeee +selection_background #dadada +selection_foreground #444444 +url_color #005fff + +# Tabs +active_tab_background #005fff +active_tab_foreground #eeeeee +inactive_tab_background #dadada +inactive_tab_foreground #9e9e9e + +# normal +color0 #444444 +color1 #ff0000 +color2 #00af5f +color3 #d75f00 +color4 #005fff +color5 #5f5f87 +color6 #afd7ff +color7 #eeeeee + +# bright +color8 #444444 +color9 #ff0000 +color10 #00af5f +color11 #d75f00 +color12 #005fff +color13 #5f5f87 +color14 #afd7ff +color15 #eeeeee diff --git a/macos/kitty/.config/kitty/kitty.app.icns b/macos/kitty/.config/kitty/kitty.app.icns new file mode 100644 index 0000000..6573b4c Binary files /dev/null and b/macos/kitty/.config/kitty/kitty.app.icns differ diff --git a/macos/kitty/.config/kitty/kitty.conf b/macos/kitty/.config/kitty/kitty.conf new file mode 100644 index 0000000..458b0ad --- /dev/null +++ b/macos/kitty/.config/kitty/kitty.conf @@ -0,0 +1,73 @@ +include invero.conf + +# term xterm-256color +enable_audio_bell no +cursor_shape block +wheel_scroll_multiplier 1.0 +touch_scroll_multiplier 1.0 +wheel_scroll_min_lines 1 +shell_integration no-cursor +cursor_blink_interval 0 + +remember_window_position yes +remember_window_size yes + +# Font +font_family Maple Mono +font_size 13.0 +# disable_ligatures always + +# undercurl_style thick-sparse + +modify_font cell_width 94% +modify_font cell_height -2px +# modify_font baseline 2px + +# modify_font underline_thickness 180% +# modify_font underline_position 2px +# modify_font strikethrough_positon 2px +text_composition_strategy legacy +# underline_exclusion 0 + +placement_strategy top +window_margin_width 0 0 +window_padding_width 0 4 + +# modify_font cell_height -1 +# modify_font cell_width 90% + +# Navigation / editing +# Make Option act as Alt on macOS +macos_option_as_alt yes + +# Use explicit bytes (no ambiguity), not \x1bb etc. +map opt+left send_text all \x1b\x62 +map opt+right send_text all \x1b\x66 +map cmd+left send_text all \x01 +map cmd+right send_text all \x05 +map opt+backspace send_text all \x1b\x7f +map cmd+backspace send_text all \x15 +map opt+delete send_text all \x1b\x64 +map cmd+delete send_text all \x0b + +# New window / tab +map cmd+n new_os_window +map cmd+t new_tab + +map cmd+1 goto_tab 1 +map cmd+2 goto_tab 2 +map cmd+3 goto_tab 3 +map cmd+4 goto_tab 4 +map cmd+5 goto_tab 5 +map cmd+6 goto_tab 6 +map cmd+7 goto_tab 7 +map cmd+8 goto_tab 8 +map cmd+9 goto_tab 9 + + +# BEGIN_KITTY_FONTS +# font_family family="JetBrains Mono" +bold_font auto +italic_font auto +bold_italic_font auto +# END_KITTY_FONTS diff --git a/macos/linearmouse/.config/linearmouse/linearmouse.json b/macos/linearmouse/.config/linearmouse/linearmouse.json new file mode 100644 index 0000000..5a5fab1 --- /dev/null +++ b/macos/linearmouse/.config/linearmouse/linearmouse.json @@ -0,0 +1,44 @@ +{ + "$schema": "https:\/\/schema.linearmouse.app\/0.10.0", + "schemes": [ + { + "if" : { + "device" : { + "vendorID" : "0x46d", + "productID" : "0xc52b", + "productName" : "USB Receiver", + "category" : "mouse" + } + }, + "scrolling": { + "reverse": { + "vertical": true + }, + "speed": { + "vertical": 0 + }, + "acceleration": { + "vertical": 1 + }, + "distance": { + "vertical": "100px" + }, + "modifiers": { + "vertical": { + "command": { + "type": "preventDefault" + } + } + } + }, + "buttons": { + "universalBackForward": true + }, + "pointer": { + "acceleration": 0.3, + "speed": 0.2, + "disableAcceleration": false + } + } + ] +} diff --git a/macos/rectangle/.config/rectangle/RectangleConfig.json b/macos/rectangle/.config/rectangle/RectangleConfig.json new file mode 100644 index 0000000..d269f8a --- /dev/null +++ b/macos/rectangle/.config/rectangle/RectangleConfig.json @@ -0,0 +1,286 @@ +{ + "bundleId" : "com.knollsoft.Rectangle", + "defaults" : { + "SUEnableAutomaticChecks" : { + "bool" : true + }, + "allowAnyShortcut" : { + "bool" : true + }, + "almostMaximizeHeight" : { + "float" : 0 + }, + "almostMaximizeWidth" : { + "float" : 0 + }, + "altThirdCycle" : { + "int" : 0 + }, + "alternateDefaultShortcuts" : { + "bool" : true + }, + "alwaysAccountForStage" : { + "int" : 0 + }, + "applyGapsToMaximize" : { + "int" : 0 + }, + "applyGapsToMaximizeHeight" : { + "int" : 0 + }, + "attemptMatchOnNextPrevDisplay" : { + "int" : 0 + }, + "autoMaximize" : { + "int" : 0 + }, + "cascadeAllDeltaSize" : { + "float" : 30 + }, + "centerHalfCycles" : { + "int" : 0 + }, + "centeredDirectionalMove" : { + "int" : 0 + }, + "cornerSnapAreaSize" : { + "float" : 20 + }, + "curtainChangeSize" : { + "int" : 0 + }, + "cycleSizesIsChanged" : { + "bool" : false + }, + "disabledApps" : { + + }, + "doubleClickTitleBar" : { + "int" : 0 + }, + "doubleClickTitleBarIgnoredApps" : { + + }, + "doubleClickTitleBarRestore" : { + "int" : 0 + }, + "dragFromStage" : { + "int" : 0 + }, + "enhancedUI" : { + "int" : 1 + }, + "footprintAlpha" : { + "float" : 0.3 + }, + "footprintAnimationDurationMultiplier" : { + "float" : 0 + }, + "footprintBorderWidth" : { + "float" : 2 + }, + "footprintColor" : { + + }, + "footprintFade" : { + "int" : 0 + }, + "fullIgnoreBundleIds" : { + + }, + "gapSize" : { + "float" : 0 + }, + "hapticFeedbackOnSnap" : { + "int" : 0 + }, + "hideMenubarIcon" : { + "bool" : false + }, + "ignoreDragSnapToo" : { + "int" : 0 + }, + "ignoredSnapAreas" : { + "int" : 0 + }, + "landscapeSnapAreas" : { + "string" : "[4,{\"compound\":-2},1,{\"action\":15},2,{\"action\":2},6,{\"action\":13},7,{\"compound\":-4},8,{\"action\":14},3,{\"action\":16},5,{\"compound\":-3}]" + }, + "launchOnLogin" : { + "bool" : true + }, + "minimumWindowHeight" : { + "float" : 0 + }, + "minimumWindowWidth" : { + "float" : 0 + }, + "missionControlDragging" : { + "int" : 0 + }, + "missionControlDraggingAllowedOffscreenDistance" : { + "float" : 25 + }, + "missionControlDraggingDisallowedDuration" : { + "int" : 250 + }, + "moveCursor" : { + "int" : 0 + }, + "moveCursorAcrossDisplays" : { + "int" : 0 + }, + "notifiedOfProblemApps" : { + "bool" : false + }, + "obtainWindowOnClick" : { + "int" : 0 + }, + "portraitSnapAreas" : { + + }, + "relaunchOpensMenu" : { + "bool" : false + }, + "resizeOnDirectionalMove" : { + "bool" : false + }, + "screenEdgeGapBottom" : { + "float" : 0 + }, + "screenEdgeGapLeft" : { + "float" : 0 + }, + "screenEdgeGapRight" : { + "float" : 0 + }, + "screenEdgeGapTop" : { + "float" : 0 + }, + "screenEdgeGapTopNotch" : { + "float" : 0 + }, + "screenEdgeGapsOnMainScreenOnly" : { + "bool" : false + }, + "selectedCycleSizes" : { + "int" : 0 + }, + "shortEdgeSnapAreaSize" : { + "float" : 145 + }, + "showAllActionsInMenu" : { + "int" : 0 + }, + "sixthsSnapArea" : { + "int" : 0 + }, + "sizeOffset" : { + "float" : 0 + }, + "snapEdgeMarginBottom" : { + "float" : 5 + }, + "snapEdgeMarginLeft" : { + "float" : 5 + }, + "snapEdgeMarginRight" : { + "float" : 5 + }, + "snapEdgeMarginTop" : { + "float" : 5 + }, + "snapModifiers" : { + "int" : 0 + }, + "specifiedHeight" : { + "float" : 1050 + }, + "specifiedWidth" : { + "float" : 1680 + }, + "stageSize" : { + "float" : 190 + }, + "subsequentExecutionMode" : { + "int" : 1 + }, + "systemWideMouseDown" : { + "int" : 0 + }, + "systemWideMouseDownApps" : { + + }, + "todo" : { + "int" : 0 + }, + "todoApplication" : { + + }, + "todoMode" : { + "bool" : false + }, + "todoSidebarSide" : { + "int" : 1 + }, + "todoSidebarWidth" : { + "float" : 400 + }, + "traverseSingleScreen" : { + "int" : 0 + }, + "unsnapRestore" : { + "int" : 0 + }, + "windowSnapping" : { + "int" : 0 + } + }, + "shortcuts" : { + "bottomHalf" : { + "keyCode" : 38, + "modifierFlags" : 786432 + }, + "firstThird" : { + "keyCode" : 33, + "modifierFlags" : 786432 + }, + "firstTwoThirds" : { + "keyCode" : 33, + "modifierFlags" : 917504 + }, + "lastThird" : { + "keyCode" : 30, + "modifierFlags" : 786432 + }, + "lastTwoThirds" : { + "keyCode" : 30, + "modifierFlags" : 917504 + }, + "leftHalf" : { + "keyCode" : 4, + "modifierFlags" : 786432 + }, + "maximize" : { + "keyCode" : 36, + "modifierFlags" : 786432 + }, + "reflowTodo" : { + "keyCode" : 45, + "modifierFlags" : 786432 + }, + "rightHalf" : { + "keyCode" : 37, + "modifierFlags" : 786432 + }, + "toggleTodo" : { + "keyCode" : 11, + "modifierFlags" : 786432 + }, + "topHalf" : { + "keyCode" : 40, + "modifierFlags" : 786432 + } + }, + "version" : "92" +} \ No newline at end of file diff --git a/macos/rectangle/.config/rectangle/com.knollsoft.Rectangle.plist b/macos/rectangle/.config/rectangle/com.knollsoft.Rectangle.plist new file mode 100644 index 0000000..ec7b8e8 Binary files /dev/null and b/macos/rectangle/.config/rectangle/com.knollsoft.Rectangle.plist differ diff --git a/macos/setup/.local/share/setup/macos-brew-backup b/macos/setup/.local/share/setup/macos-brew-backup new file mode 100755 index 0000000..145f931 --- /dev/null +++ b/macos/setup/.local/share/setup/macos-brew-backup @@ -0,0 +1,10 @@ +#!/bin/bash + +BACKUP_FILE="$HOME/.config/homebrew/Brewfile" + +echo "Backing up Homebrew installations..." +mkdir -p "$(dirname "$BACKUP_FILE")" + +brew bundle dump --file="$BACKUP_FILE" --force + +echo "Backup saved to $BACKUP_FILE" diff --git a/macos/setup/.local/share/setup/macos-brew-restore b/macos/setup/.local/share/setup/macos-brew-restore new file mode 100755 index 0000000..19d85af --- /dev/null +++ b/macos/setup/.local/share/setup/macos-brew-restore @@ -0,0 +1,13 @@ +#!/bin/bash + +BACKUP_FILE="$HOME/.config/homebrew/Brewfile" + +if [[ ! -f "$BACKUP_FILE" ]]; then + echo "Backup file not found: $BACKUP_FILE" + exit 1 +fi + +echo "Restoring Homebrew installations..." +brew bundle --file="$BACKUP_FILE" + +echo "Homebrew restoration complete." diff --git a/macos/setup/.local/share/setup/macos-defaults b/macos/setup/.local/share/setup/macos-defaults new file mode 100755 index 0000000..a7c307f --- /dev/null +++ b/macos/setup/.local/share/setup/macos-defaults @@ -0,0 +1,196 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Close any open System Preferences panes, to prevent them from overriding +# settings we’re about to change +osascript -e 'tell application "System Preferences" to quit' + +# Ask for the administrator password upfront +sudo -v + +# Keep-alive: update existing `sudo` time stamp until `.macos` has finished +while true; do + sudo -n true + sleep 60 + kill -0 "$$" || exit +done 2>/dev/null & + +# Save to disk (not to iCloud) by default +defaults write NSGlobalDomain NSDocumentSaveNewDocumentsToCloud -bool false + +# Disable the “Are you sure you want to open this application?” dialog +defaults write com.apple.LaunchServices LSQuarantine -bool false + +# Disable Typing features +defaults write NSGlobalDomain NSAutomaticCapitalizationEnabled -bool false +defaults write NSGlobalDomain NSAutomaticDashSubstitutionEnabled -bool false +defaults write NSGlobalDomain NSAutomaticPeriodSubstitutionEnabled -bool false +defaults write NSGlobalDomain NSAutomaticQuoteSubstitutionEnabled -bool false +defaults write NSGlobalDomain NSAutomaticSpellingCorrectionEnabled -bool false + +# Disable press-and-hold for keys in favor of key repeat +defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool false +defaults write NSGlobalDomain KeyRepeat -int 2 +defaults write NSGlobalDomain InitialKeyRepeat -int 15 + +defaults write NSGlobalDomain AppleLanguages -array "en" "es" "bg" +defaults write NSGlobalDomain AppleLocale -string "en_US@rg=eszzzz" + +## Finder + +# Screenshots/captures +defaults write com.apple.screencapture location -string "${HOME}/Pictures/Screenshots" +defaults write com.apple.screencapture style -string "display" +defaults write com.apple.screencapture target -string "file" +defaults write com.apple.screencapture video -int 1 + +# Finder +# Interface elements +defaults write com.apple.finder ShowPathbar -bool true +defaults write com.apple.finder ShowStatusBar -bool true +defaults write com.apple.finder ShowSidebar -bool true +defaults write com.apple.finder ShowRecentTags -bool false + +# View and sorting +defaults write com.apple.finder FXPreferredViewStyle -string "Nlsv" +defaults write com.apple.finder FXPreferredSearchViewStyle -string "Nlsv" +defaults write com.apple.finder _FXSortFoldersFirst -bool true +defaults write com.apple.finder FXPreferredGroupBy -string "None" +defaults write com.apple.finder FXDefaultSearchScope -string "SCcf" + +# Behavior +defaults write com.apple.finder NewWindowTarget -string "PfHm" +defaults write com.apple.finder FXOpenFoldersInTabs -bool true +defaults write com.apple.finder FXRemoveOldTrashItems -bool false +defaults write com.apple.finder FXShowAllExtensions -bool true +defaults write com.apple.finder FXEnableExtensionChangeWarning -bool true +defaults write com.apple.finder FXRemoveICloudDriveWarning -bool true +defaults write com.apple.finder FXWarnBeforeEmptyingTrash -bool true + +# Desktop icons (none) +defaults write com.apple.finder ShowHardDrivesOnDesktop -bool false +defaults write com.apple.finder ShowExternalHardDrivesOnDesktop -bool false +defaults write com.apple.finder ShowRemovableMediaOnDesktop -bool false +defaults write com.apple.finder ShowConnectedServersOnDesktop -bool false + +# Tags +defaults write com.apple.finder FavoriteTagNames -array + +# iCloud +defaults write com.apple.finder FXICloudDriveEnabled -bool false + +# Finder: show all filename extensions +defaults write NSGlobalDomain AppleShowAllExtensions -bool true + +# Avoid creating .DS_Store files on network or USB volumes +defaults write com.apple.desktopservices DSDontWriteNetworkStores -bool true +defaults write com.apple.desktopservices DSDontWriteUSBStores -bool true + +## Dock + +#!/bin/bash +set -euo pipefail + +# Reset preferences +# rm -f ~/Library/Preferences/com.apple.dock.plist + +# Reset Dock layout +defaults write com.apple.dock persistent-apps -array +defaults write com.apple.dock persistent-others -array + +# Basic Dock preferences +defaults write com.apple.dock autohide -bool true +defaults write com.apple.dock autohide-delay -float 0 +defaults write com.apple.dock autohide-time-modifier -float 0.4 +defaults write com.apple.dock enterMissionControlByTopWindowDrag -bool false +defaults write com.apple.dock expose-group-apps -bool true +defaults write com.apple.dock mineffect -string "scale" +defaults write com.apple.dock minimize-to-application -bool false +defaults write com.apple.dock orientation -string "bottom" +defaults write com.apple.dock show-process-indicators -bool false +defaults write com.apple.dock show-recents -bool false +defaults write com.apple.dock showAppExposeGestureEnabled -bool true +defaults write com.apple.dock showDesktopGestureEnabled -bool false +defaults write com.apple.dock showLaunchpadGestureEnabled -bool false +defaults write com.apple.dock tilesize -int 38 + +# Add Brave Browser +defaults write com.apple.dock persistent-apps -array-add \ + " + tile-data + + file-data + + _CFURLString + /Applications/Brave Browser.app + _CFURLStringType + 0 + + + tile-type + file-tile +" + +# Add Ghostty +defaults write com.apple.dock persistent-apps -array-add \ + " + tile-data + + file-data + + _CFURLString + /Applications/Ghostty.app + _CFURLStringType + 0 + + + tile-type + file-tile +" + +# Add Screenshots directory (display as folder, show as grid) +defaults write com.apple.dock persistent-others -array-add \ + " + tile-data + + displayas + 1 + showas + 2 + file-data + + _CFURLString + /Users/tomas/Pictures/Screenshots + _CFURLStringType + 0 + + + tile-type + directory-tile +" + +# Add Downloads directory (display as folder, show as grid) +defaults write com.apple.dock persistent-others -array-add \ + " + tile-data + + displayas + 1 + showas + 2 + file-data + + _CFURLString + /Users/tomas/Downloads + _CFURLStringType + 0 + + + tile-type + directory-tile +" + +# Apply changes +killall Finder &>/dev/null +killall Dock &>/dev/null +echo "Setup complete." diff --git a/macos/sol/.config/sol/mmkv.default b/macos/sol/.config/sol/mmkv.default new file mode 100755 index 0000000..9dec454 Binary files /dev/null and b/macos/sol/.config/sol/mmkv.default differ diff --git a/macos/sol/.config/sol/mmkv.default.crc b/macos/sol/.config/sol/mmkv.default.crc new file mode 100755 index 0000000..5a527cc Binary files /dev/null and b/macos/sol/.config/sol/mmkv.default.crc differ diff --git a/macos/wezterm/.config/wezterm/colors/Invero Day.toml b/macos/wezterm/.config/wezterm/colors/Invero Day.toml new file mode 100644 index 0000000..95c7201 --- /dev/null +++ b/macos/wezterm/.config/wezterm/colors/Invero Day.toml @@ -0,0 +1,36 @@ +[colors] +background = "#eeeeee" +foreground = "#444444" +cursor_bg = "#005fff" +cursor_border = "#9e9e9e" +cursor_fg = "#eeeeee" +selection_bg = "#dadada" +selection_fg = "#444444" +split = "#005fff" +compose_cursor = "#d75f00" +scrollbar_thumb = "#9e9e9e" + +copy_mode_active_highlight_bg = { Color = "#dadada" } +copy_mode_active_highlight_fg = { Color = "#d75f00" } +copy_mode_inactive_highlight_bg = { Color = "#eeeeee" } +copy_mode_inactive_highlight_fg = { Color = "#d75f00" } + +ansi = ["#444444", "#ff0000", "#00af5f", "#d75f00", "#005fff", "#5f5f87", "#afd7ff", "#eeeeee"] +brights = ["#444444", "#ff0000", "#00af5f", "#d75f00", "#005fff", "#5f5f87", "#afd7ff", "#eeeeee"] + +[colors.tab_bar] +inactive_tab_edge = "#ff0000" +background = "#444444" + +[colors.tab_bar.active_tab] +fg_color = "#eeeeee" +bg_color = "#444444" +intensity = "Bold" + +[colors.tab_bar.inactive_tab] +fg_color = "#9e9e9e" +bg_color = "#444444" + +[colors.tab_bar.inactive_tab_hover] +fg_color = "#dadada" +bg_color = "#444444" diff --git a/macos/wezterm/.config/wezterm/wezterm.lua b/macos/wezterm/.config/wezterm/wezterm.lua new file mode 100644 index 0000000..1f47767 --- /dev/null +++ b/macos/wezterm/.config/wezterm/wezterm.lua @@ -0,0 +1,72 @@ +local wezterm = require("wezterm") +local config = wezterm.config_builder() +local act = wezterm.action + +-- General +config.term = "wezterm" +config.color_scheme = "Invero Day" +config.use_ime = false + +-- Font +config.font = wezterm.font({ family = "Maple Mono NF", weight = "Medium" }) +config.font_size = 13 +config.harfbuzz_features = { "calt=0", "clig=0", "liga=0" } -- disables alternates and ligatures +config.underline_position = -4 +config.underline_thickness = 3 + +-- Appearance +config.bold_brightens_ansi_colors = false +config.window_padding = { left = "0.5cell", right = "0.5cell", top = 6, bottom = 0 } +config.window_content_alignment = { horizontal = "Center", vertical = "Top" } +config.cell_width = 0.9 +config.line_height = 0.9 + +-- Tabs +config.use_fancy_tab_bar = false +config.show_new_tab_button_in_tab_bar = false +config.hide_tab_bar_if_only_one_tab = true + +-- Events +wezterm.on("toggle-tabbar", function(window, _) + local overrides = window:get_config_overrides() or {} + if overrides.enable_tab_bar == false then + wezterm.log_info("tab bar shown") + overrides.enable_tab_bar = true + else + wezterm.log_info("tab bar hidden") + overrides.enable_tab_bar = false + end + window:set_config_overrides(overrides) +end) + +-- Keybindings +config.keys = { + { mods = "OPT", key = "LeftArrow", action = act.SendString("\x1bb") }, -- Jump back one word + { mods = "OPT", key = "RightArrow", action = act.SendString("\x1bf") }, -- Jump forward one word + { mods = "CMD", key = "LeftArrow", action = act.SendString("\x01") }, -- Move to start of line + { mods = "CMD", key = "RightArrow", action = act.SendString("\x05") }, -- Move to end of line + { mods = "OPT", key = "Backspace", action = act.SendString("\x1b\x7f") }, -- Delete previous word + { mods = "CMD", key = "Backspace", action = act.SendString("\x15") }, -- Delete previous line + { mods = "OPT", key = "Delete", action = act.SendString("\x1bd") }, -- Delete next word + { mods = "CMD", key = "Delete", action = act.SendString("\x0b") }, -- Delete next line + { mods = "CMD", key = "n", action = act.SpawnWindow }, -- New window + { mods = "CMD", key = "t", action = act.SpawnCommandInNewTab({ cwd = wezterm.home_dir }) }, -- New tab + { mods = "SUPER|SHIFT", key = "LeftArrow", action = act({ MoveTabRelative = -1 }) }, -- Move tab left + { mods = "SUPER|SHIFT", key = "RightArrow", action = act({ MoveTabRelative = 1 }) }, -- Move tab right + { mods = "SUPER|SHIFT", key = "b", action = act.EmitEvent("toggle-tabbar") }, + { mods = "SUPER|SHIFT", key = "o", action = wezterm.action.ShowTabNavigator }, + { + mods = "SUPER|SHIFT", + key = "r", + action = wezterm.action.PromptInputLine({ + description = "Enter new tab title", + action = wezterm.action_callback(function(window, pane, line) + if line then + window:active_tab():set_title(line) + end + end), + }), + }, +} + +return config