diff --git a/README.md b/README.md new file mode 100644 index 0000000..88bba32 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# PS1 dotfiles (Linux/WSL global + macOS user) + +## Hva du får +- 2-soners prompt (zone1: dato/tid/user i grå-blå pastell, zone2: host/path i turkis/sval grønn) +- tidsbasert emoji (Europe/Oslo) +- path forkorting med `/home/user` / `/Users/user` synlig +- Nerd Font powerline glyphs når mulig, fallback hvis ikke +- disable per bruker + +## Install + +### Linux / WSL (global) +```bash +sudo bash install/install-linux-global.sh + + + + diff --git a/bannerscript.sh b/bannerscript.sh new file mode 100644 index 0000000..8a5a8db --- /dev/null +++ b/bannerscript.sh @@ -0,0 +1,119 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Must be root +if [[ "${EUID}" -ne 0 ]]; then + echo "Kjør som root: sudo bash $0" + exit 1 +fi + +### 1) Ensure required packages installed +need_pkgs=(landscape-common figlet lolcat) +missing=() +for p in "${need_pkgs[@]}"; do + if ! dpkg -s "$p" >/dev/null 2>&1; then + missing+=("$p") + fi +done + +if ((${#missing[@]} > 0)); then + echo "Installerer manglende pakker: ${missing[*]}" + export DEBIAN_FRONTEND=noninteractive + apt-get update -y + apt-get install -y "${missing[@]}" +fi + +### 2) Ask punchline (clear wording, always from TTY) +echo +read -r -p "Skriv ønsket punchline (vises under hostname i banneret): " PUNCHLINE /dev/null || true + +# Remove old managed block (if any) +tmp="$(mktemp)" +awk -v s="$MARKER_START" -v e="$MARKER_END" ' + $0==s {inside=1; next} + $0==e {inside=0; next} + !inside {print} +' "$GLOBAL_BASHRC" > "$tmp" +cat "$tmp" > "$GLOBAL_BASHRC" +rm -f "$tmp" + +# Append fresh managed block (NO expansion while writing) +cat >> "$GLOBAL_BASHRC" <<'EOF' + +# >>> global custom bashrc (managed) >>> +# Only run in interactive shells +case "$-" in + *i*) ;; + *) return ;; +esac + +##### HISTORY / BASICS ##### +HISTCONTROL=ignoreboth +shopt -s histappend +HISTSIZE=999999 +HISTFILESIZE=999999 +shopt -s checkwinsize + +[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)" + +##### PROMPT (GLOBAL) ##### +export PS1="\[\e[0m\]\[\e[38;5;35m\]╭─(\[\e[38;5;38m\]\t\[\e[38;5;35m\])-(\[\e[38;5;38m\]\j\[\e[38;5;35m\])-(\[\e[38;5;38m\]\H\[\e[38;5;35m\])-(\[\e[38;5;38m\]\w\[\e[38;5;35m\])\n\[\e[38;5;35m\]╰──🚀 \[\e[0m\]" + +##### COLORS / ALIASES ##### +if [ -x /usr/bin/dircolors ]; then + test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" + alias ls='ls --color=auto' + alias grep='grep --color=auto' + alias fgrep='fgrep --color=auto' + alias egrep='egrep --color=auto' +fi + +alias ll='ls -alF' +alias la='ls -A' +alias l='ls -CF' + +if [ -f ~/.bash_aliases ]; then + . ~/.bash_aliases +fi + +if ! shopt -oq posix; then + if [ -f /usr/share/bash-completion/bash_completion ]; then + . /usr/share/bash-completion/bash_completion + elif [ -f /etc/bash_completion ]; then + . /etc/bash_completion + fi +fi + +##### BANNER ##### +LOLCAT="/usr/games/lolcat" +if [ ! -x "$LOLCAT" ]; then + LOLCAT="$(command -v lolcat 2>/dev/null || true)" +fi +[ -z "$LOLCAT" ] && LOLCAT="cat" + +figlet "$(hostname)" -c | "$LOLCAT" +figlet -f digital "__PUNCHLINE__" -c | "$LOLCAT" +landscape-sysinfo | "$LOLCAT" +# <<< global custom bashrc (managed) <<< + +EOF + +# Replace placeholder with user punchline (safe) +safe_punchline="$(printf '%s' "$PUNCHLINE" | sed 's/[\/&]/\\&/g')" +sed -i "s/__PUNCHLINE__/${safe_punchline}/g" "$GLOBAL_BASHRC" + +echo +echo "Ferdig ✅" +echo "Global PS1 + banner er nå aktivt for alle brukere." +echo "Kjør scriptet på nytt for å endre punchline." diff --git a/bashscript.sh b/bashscript.sh deleted file mode 100644 index 008de67..0000000 --- a/bashscript.sh +++ /dev/null @@ -1,147 +0,0 @@ -#!/usr/bin/env bash - -# Must be sourced -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - echo "Bruk: source ps1.sh" - exit 1 -fi - -case "$-" in - *i*) ;; - *) return 0 ;; -esac - -# ------------------------------------------------- -# Nerd Font detection + fallback controls -# -# Env overrides: -# PS1_FORCE_ASCII=1 -> always use ASCII/Unicode fallback separators -# PS1_FORCE_NF=1 -> always use Nerd Font separators -# ------------------------------------------------- -_ps1_has_nf() { - # Explicit overrides win - if [[ "${PS1_FORCE_ASCII:-0}" == "1" ]]; then - return 1 - fi - if [[ "${PS1_FORCE_NF:-0}" == "1" ]]; then - return 0 - fi - - # Heuristic: if fc-list exists and shows a Nerd Font family, assume NF works. - if command -v fc-list >/dev/null 2>&1; then - # Keep it generic: any "Nerd Font" is good enough - if fc-list 2>/dev/null | grep -qi "Nerd Font"; then - return 0 - fi - fi - - # Otherwise: safe fallback - return 1 -} - -# ------------------------------------------------- -# Time-based emoji (Europe/Oslo) -# ------------------------------------------------- -_ps1_symbol() { - local h m hh mm - hh=$(TZ=Europe/Oslo date +%H); mm=$(TZ=Europe/Oslo date +%M) - h=$((10#$hh)); m=$((10#$mm)) - - if (( h >= 5 && h <= 8 )); then echo "🌅" # tidlig - elif (( h >= 9 && h <= 10 )); then echo "☕" # formiddag - elif (( h == 11 && m < 30 )); then echo "🥪" # lunsj - elif (( (h == 11 && m >= 30) || (h >= 12 && h <= 15) )); then echo "💻" # dag - elif (( h == 16 )); then echo "🍲" # middag - elif (( h >= 17 && h <= 22 )); then echo "🌆" # kveld - else echo "🌙" # natt - fi -} - -# ------------------------------------------------- -# Path shortening (keeps /home/user visible) -# Compatible with bash 3.2+ (no negative array indexes) -# ------------------------------------------------- -_ps1_path() { - local p="$PWD" - local parts=() - IFS='/' read -ra parts <<< "$p" - - # If short, keep full - if ((${#parts[@]} < 6)); then - echo "$p" - return - fi - - # / + first two comps + … + last two comps - local n=${#parts[@]} - local a="${parts[1]}" - local b="${parts[2]}" - local c="${parts[$((n-2))]}" - local d="${parts[$((n-1))]}" - - echo "/${a}/${b}/…/${c}/${d}" -} - -# ------------------------------------------------- -# Dynamic vars updated before each prompt -# ------------------------------------------------- -__PS1_SYM="" -__PS1_PATH="" -__PS1_STATUS=0 -__PS1_USE_NF=0 - -_ps1_update() { - __PS1_STATUS=$? - __PS1_SYM="$(_ps1_symbol)" - __PS1_PATH="$(_ps1_path)" - if _ps1_has_nf; then __PS1_USE_NF=1; else __PS1_USE_NF=0; fi -} - -# ------------------------------------------------- -# Enable / disable -# ------------------------------------------------- -__PS1_PREV_PROMPT_COMMAND="${PROMPT_COMMAND-}" - -ps1_on() { - PROMPT_COMMAND="_ps1_update" - - local RST="\[\e[0m\]" - - # Two-zone palette (as requested) - # Zone 1: gray-blue pastel (date/time/user) - local Z1_BG="\[\e[48;5;61m\]" - local Z1_FG="\[\e[38;5;255m\]" - - # Zone 2: turquoise / cool green (host/path) - local Z2_BG="\[\e[48;5;37m\]" - local Z2_FG="\[\e[38;5;255m\]" - local PATH_FG="\[\e[38;5;194m\]" - - # Frame line - local FRAME="\[\e[38;5;60m\]" - - # Status colors - local OK="\[\e[38;5;76m\]" - local BAD="\[\e[38;5;203m\]" - - # Build separators based on NF availability (evaluated at prompt time via $__PS1_USE_NF) - # We avoid embedding raw variables that bash re-parses weirdly by using $(...) inside PS1 only for - # selecting literal separators (safe), while keeping all color escapes static and balanced. - local SEP_EXPR='\$( [ "$__PS1_USE_NF" -eq 1 ] && printf "" || printf "▶" )' - local LEFT_EXPR='\$( [ "$__PS1_USE_NF" -eq 1 ] && printf "" || printf "[" )' - local RIGHT_EXPR='\$( [ "$__PS1_USE_NF" -eq 1 ] && printf "" || printf "]" )' - - local PROMPT_SYM="\$( [ \$__PS1_STATUS -eq 0 ] && printf '${OK}' || printf '${BAD}' )➜${RST}" - - # Keep exact order: date -> time -> user -> host -> path, then newline, then prompt - # Note: \d, \A, \u, \h are PS1 escapes. - PS1="\ -${FRAME}╭─${RST}\ -${Z1_BG}${Z1_FG}${LEFT_EXPR} \d \A \u ${RST}${Z1_BG}${Z2_BG}${Z2_FG}${SEP_EXPR}${RST}\ -${Z2_BG}${Z2_FG} @\h ${PATH_FG}\${__PS1_PATH} ${RST}${Z2_BG}${Z2_FG}${RIGHT_EXPR}${RST}\ -\n${FRAME}╰── ${RST}${PROMPT_SYM} \${__PS1_SYM} " -} - -ps1_off() { - PROMPT_COMMAND="$__PS1_PREV_PROMPT_COMMAND" -} diff --git a/install-global-ps1.sh b/dotfiles/install/install-global-ps1.sh similarity index 100% rename from install-global-ps1.sh rename to dotfiles/install/install-global-ps1.sh diff --git a/dotfiles/install/install-linux-global.sh b/dotfiles/install/install-linux-global.sh new file mode 100644 index 0000000..698f0bb --- /dev/null +++ b/dotfiles/install/install-linux-global.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ "${EUID}" -ne 0 ]]; then + echo "Kjør som root: sudo bash $0" + exit 1 +fi + +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +SRC_PS1="$REPO_ROOT/ps1/ps1.sh" +DST_PS1="/etc/profile.d/ps1.sh" +BASH_BASHRC="/etc/bash.bashrc" + +MARKER_START="# >>> global ps1 (managed) >>>" +MARKER_END="# <<< global ps1 (managed) <<<" + +if [[ ! -r "$SRC_PS1" ]]; then + echo "Fant ikke $SRC_PS1" + exit 1 +fi + +cp -f "$SRC_PS1" "$DST_PS1" +chmod 0644 "$DST_PS1" + +# Ensure /etc/bash.bashrc sources it (idempotent) +tmp="$(mktemp)" +awk -v s="$MARKER_START" -v e="$MARKER_END" ' + $0==s {inside=1; next} + $0==e {inside=0; next} + !inside {print} +' "$BASH_BASHRC" > "$tmp" +cat "$tmp" > "$BASH_BASHRC" +rm -f "$tmp" + +cat >> "$BASH_BASHRC" < "$tmp" + cat "$tmp" > "$file" + rm -f "$tmp" + + cat >> "$file" </dev/null 2>&1; then + echo "🍺 Homebrew funnet – prøver cask..." + brew tap homebrew/cask-fonts >/dev/null 2>&1 || true + if brew install --cask font-jetbrains-mono-nerd-font; then + echo "✅ Installert via Homebrew cask." + echo "Velg fonten i terminal: JetBrainsMono Nerd Font" + exit 0 + fi + echo "⚠️ Cask feilet, faller tilbake til manuell install." + fi + + FONT_DIR="$HOME/Library/Fonts" + mkdir -p "$FONT_DIR" + tmpzip="$(mktemp -t jbmono.XXXXXX.zip)" + curl -fsSL "$FONT_ZIP_URL" -o "$tmpzip" + tmpdir="$(mktemp -d -t jbmono.XXXXXX)" + unzip -o "$tmpzip" -d "$tmpdir" >/dev/null + rm -f "$tmpzip" + + # Copy only TTF/OTF + find "$tmpdir" -type f \( -iname "*.ttf" -o -iname "*.otf" \) -maxdepth 2 -print0 \ + | xargs -0 -I{} cp -f "{}" "$FONT_DIR"/ + rm -rf "$tmpdir" + + echo "✅ Font kopiert til $FONT_DIR" + echo "Velg fonten i terminal: JetBrainsMono Nerd Font" + exit 0 +fi + +# Linux +FONT_DIR="$HOME/.local/share/fonts" +mkdir -p "$FONT_DIR" +tmpzip="$(mktemp --suffix=.zip)" +curl -fsSL "$FONT_ZIP_URL" -o "$tmpzip" +unzip -o "$tmpzip" -d "$FONT_DIR" >/dev/null +rm -f "$tmpzip" +fc-cache -f >/dev/null 2>&1 || true +echo "✅ Font installert i $FONT_DIR" +echo "Velg fonten i terminal: JetBrainsMono Nerd Font" diff --git a/dotfiles/ps1/ps1.sh b/dotfiles/ps1/ps1.sh new file mode 100644 index 0000000..d0f3d02 --- /dev/null +++ b/dotfiles/ps1/ps1.sh @@ -0,0 +1,121 @@ +#!/usr/bin/env bash +# PS1 module with Nerd Font fallback + disable switch +# Designed for bash 3.2+ (macOS) and bash 4/5 (Linux) +# Load this in interactive shells. + +case "$-" in + *i*) ;; + *) return 0 ;; +esac + +# Disable switch (per-user/per-shell) +# 1) env: DISABLE_GLOBAL_PS1=1 +# 2) file: ~/.config/ps1/disable +if [[ "${DISABLE_GLOBAL_PS1:-0}" == "1" ]]; then + return 0 +fi +if [[ -f "$HOME/.config/ps1/disable" ]]; then + return 0 +fi + +# Nerd Font detection (best-effort) + overrides +# PS1_FORCE_ASCII=1 -> always fallback separators +# PS1_FORCE_NF=1 -> always Nerd Font separators +_ps1_has_nf() { + if [[ "${PS1_FORCE_ASCII:-0}" == "1" ]]; then return 1; fi + if [[ "${PS1_FORCE_NF:-0}" == "1" ]]; then return 0; fi + + # Linux/WSL: fontconfig often available + if command -v fc-list >/dev/null 2>&1; then + fc-list 2>/dev/null | grep -qi "Nerd Font" && return 0 + fi + + # macOS: fontconfig often not present; default to fallback unless forced + return 1 +} + +# Time-based emoji (Europe/Oslo) +_ps1_symbol() { + local hh mm h m + hh=$(TZ=Europe/Oslo date +%H); mm=$(TZ=Europe/Oslo date +%M) + h=$((10#$hh)); m=$((10#$mm)) + + if (( h >= 5 && h <= 8 )); then echo "🌅" + elif (( h >= 9 && h <= 10 )); then echo "☕" + elif (( h == 11 && m < 30 )); then echo "🥪" + elif (( (h == 11 && m >= 30) || (h >= 12 && h <= 15) )); then echo "💻" + elif (( h == 16 )); then echo "🍲" + elif (( h >= 17 && h <= 22 )); then echo "🌆" + else echo "🌙" + fi +} + +# Path shortening (keeps /home/user or /Users/user visible) +# bash 3.2 safe +_ps1_path() { + local p="$PWD" + local parts=() + IFS='/' read -ra parts <<< "$p" + + if ((${#parts[@]} < 6)); then + echo "$p"; return + fi + + local n=${#parts[@]} + echo "/${parts[1]}/${parts[2]}/…/${parts[$((n-2))]}/${parts[$((n-1))]}" +} + +# Dynamic vars updated before each prompt +__PS1_SYM="" +__PS1_PATH="" +__PS1_STATUS=0 +__PS1_USE_NF=0 + +_ps1_update() { + __PS1_STATUS=$? + __PS1_SYM="$(_ps1_symbol)" + __PS1_PATH="$(_ps1_path)" + if _ps1_has_nf; then __PS1_USE_NF=1; else __PS1_USE_NF=0; fi +} + +ps1_on() { + PROMPT_COMMAND="_ps1_update" + + local RST="\[\e[0m\]" + + # Two-zone palette + # Zone 1: gray-blue pastel (date/time/user) + local Z1_BG="\[\e[48;5;61m\]" + local Z1_FG="\[\e[38;5;255m\]" + + # Zone 2: turquoise / cool green (host/path) + local Z2_BG="\[\e[48;5;37m\]" + local Z2_FG="\[\e[38;5;255m\]" + local PATH_FG="\[\e[38;5;194m\]" + + # Frame + local FRAME="\[\e[38;5;60m\]" + + # Status colors + local OK="\[\e[38;5;76m\]" + local BAD="\[\e[38;5;203m\]" + + # Separators with fallback + local SEP_EXPR='\$( [ "$__PS1_USE_NF" -eq 1 ] && printf "" || printf "▶" )' + local LEFT_EXPR='\$( [ "$__PS1_USE_NF" -eq 1 ] && printf "" || printf "[" )' + local RIGHT_EXPR='\$( [ "$__PS1_USE_NF" -eq 1 ] && printf "" || printf "]" )' + + local PROMPT_SYM="\$( [ \$__PS1_STATUS -eq 0 ] && printf '${OK}' || printf '${BAD}' )➜${RST}" + + # Keep order: date time user | host path, newline, then prompt + PS1="\ +${FRAME}╭─${RST}\ +${Z1_BG}${Z1_FG}${LEFT_EXPR} \d \A \u ${RST}${Z1_BG}${Z2_BG}${Z2_FG}${SEP_EXPR}${RST}\ +${Z2_BG}${Z2_FG} @\h ${PATH_FG}\${__PS1_PATH} ${RST}${Z2_BG}${Z2_FG}${RIGHT_EXPR}${RST}\ +\n${FRAME}╰── ${RST}${PROMPT_SYM} \${__PS1_SYM} " +} + +ps1_off() { :; } + +# Enable by default when loaded (global standard) +ps1_on diff --git a/install-nerdfont.sh b/install-nerdfont.sh deleted file mode 100755 index bacc47c..0000000 --- a/install-nerdfont.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash -set -e - -FONT_NAME="JetBrainsMono Nerd Font" -FONT_DIR="$HOME/.local/share/fonts" -FONT_TEST="JetBrainsMonoNerdFont-Regular.ttf" - -echo "🔎 Sjekker Nerd Font..." - -if fc-list | grep -qi "JetBrainsMono Nerd Font"; then - echo "✅ Nerd Font allerede installert." - exit 0 -fi - -echo "⬇️ Laster ned JetBrainsMono Nerd Font..." - -mkdir -p "$FONT_DIR" -cd "$FONT_DIR" - -TMP_ZIP="$(mktemp --suffix=.zip)" - -curl -fsSL \ - https://github.com/ryanoasis/nerd-fonts/releases/latest/download/JetBrainsMono.zip \ - -o "$TMP_ZIP" - -echo "📦 Pakker ut fonter..." -unzip -o "$TMP_ZIP" >/dev/null -rm -f "$TMP_ZIP" - -echo "🔄 Oppdaterer font cache..." -fc-cache -f >/dev/null - -echo "🎉 Ferdig!" -echo -echo "⚠️ Viktig:" -echo " Du må nå velge «JetBrainsMono Nerd Font» i terminalen din." -echo " (Terminal → Preferences → Font)" diff --git a/ps1.sh b/ps1.sh deleted file mode 100644 index 23c8584..0000000 --- a/ps1.sh +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env bash - -# Must be sourced -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - echo "Bruk: source ps1.sh" - exit 1 -fi - -case "$-" in - *i*) ;; - *) return 0 ;; -esac - -# ------------------------------------------------- -# Time-based emoji (Europe/Oslo) -# ------------------------------------------------- -_ps1_symbol() { - local h m - h=$(TZ=Europe/Oslo date +%H); h=$((10#$h)) - m=$(TZ=Europe/Oslo date +%M); m=$((10#$m)) - - if (( h >= 5 && h <= 8 )); then echo "🌅" - elif (( h >= 9 && h <= 10 )); then echo "☕" - elif (( h == 11 && m < 30 )); then echo "🥪" - elif (( (h == 11 && m >= 30) || (h >= 12 && h <= 15) )); then echo "💻" - elif (( h == 16 )); then echo "🍲" - elif (( h >= 17 && h <= 22 )); then echo "🌆" - else echo "🌙" - fi -} - -# ------------------------------------------------- -# Path shortening -# ------------------------------------------------- -_ps1_path() { - local p="$PWD" - local parts=() - IFS='/' read -ra parts <<< "$p" - - if ((${#parts[@]} < 6)); then - echo "$p" - else - echo "/${parts[1]}/${parts[2]}/…/${parts[-2]}/${parts[-1]}" - fi -} - -# ------------------------------------------------- -# Update vars -# ------------------------------------------------- -__PS1_SYM="" -__PS1_PATH="" -__PS1_STATUS=0 - -_ps1_update() { - __PS1_STATUS=$? - __PS1_SYM="$(_ps1_symbol)" - __PS1_PATH="$(_ps1_path)" -} - -# ------------------------------------------------- -# Enable / disable -# ------------------------------------------------- -__PS1_PREV_PROMPT_COMMAND="${PROMPT_COMMAND-}" - -ps1_on() { - PROMPT_COMMAND="_ps1_update" - - local RST="\[\e[0m\]" - - # ===== ZONE 1: date / time / user ===== - local Z1_BG="\[\e[48;5;61m\]" # gray-blue pastel - local Z1_FG="\[\e[38;5;255m\]" - - # ===== ZONE 2: host / path ===== - local Z2_BG="\[\e[48;5;37m\]" # turquoise / cool green - local Z2_FG="\[\e[38;5;255m\]" - local PATH_FG="\[\e[38;5;194m\]" - - # Frame - local FRAME="\[\e[38;5;60m\]" - - # Separators - local SEP="" - local LEFT="" - local RIGHT="" - local SEP_Z1="\[\e[38;5;61m\]" - local SEP_Z2="\[\e[38;5;37m\]" - - # Status - local OK="\[\e[38;5;76m\]" - local BAD="\[\e[38;5;203m\]" - local PROMPT_SYM="\$( [ \$__PS1_STATUS -eq 0 ] && printf '${OK}' || printf '${BAD}' )➜${RST}" - - PS1="\ -${FRAME}╭─${RST}\ -${Z1_BG}${Z1_FG}${LEFT} \d \A \u ${RST}${Z1_BG}${SEP_Z2}${SEP}\ -${Z2_BG}${Z2_FG} @\h ${PATH_FG}\${__PS1_PATH} ${RST}${Z2_BG}${RIGHT}${RST}\ -\n${FRAME}╰── ${RST}${PROMPT_SYM} \${__PS1_SYM} " -} - -ps1_off() { - PROMPT_COMMAND="$__PS1_PREV_PROMPT_COMMAND" -} \ No newline at end of file