Merge features/waybar → main

Waybar island floating 3-pills, scripts GPU/réseau/profil énergie,
overlay wob-overlay.py, popup média vc-media-popup.py, fix install
GTK (adw-gtk3-dark + variables COSMIC exactes), protection root.
This commit is contained in:
Tetardtek
2026-02-23 19:02:31 +01:00
62 changed files with 4616 additions and 226 deletions

3
.gitignore vendored
View File

@@ -4,3 +4,6 @@ config.tar.gz
# Dépôt git du thème Discord (sources SCSS)
# Le CSS compilé est dans INSTALL/themes/violet-chaton.theme.css
INSTALL/themes/violet-chaton-discord-theme/
# Dépôt git du thème VSCode
# Aucune source dans le projet général en 1.0.0
INSTALL/themes/violet-chaton-VSCode-theme/

View File

@@ -0,0 +1,8 @@
[Desktop Entry]
Name=Waybar
Comment=violet-chaton status bar
Type=Application
Exec=waybar
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true

View File

@@ -0,0 +1,8 @@
[Desktop Entry]
Name=WOB
Comment=Wayland overlay bar (volume / luminosité)
Type=Application
Exec=/home/tetardtek/.config/waybar/scripts/wob-start.sh
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true

View File

@@ -0,0 +1,7 @@
configuration {
font: "JetBrainsMono Nerd Font 13";
show-icons: false;
click-to-exit: true;
}
@theme "violet-chaton"

View File

@@ -0,0 +1,18 @@
[general]
bars = 8
framerate = 30
[input]
method = pulse
source = auto
[output]
method = raw
raw_target = /dev/stdout
data_format = ascii
ascii_max_range = 8
bar_delimiter = 59
[smoothing]
gravity = 100
noise_reduction = 77

View File

@@ -0,0 +1,312 @@
{
// ── violet-chaton Waybar — island floating 3 pills ──────────────────────
// LEFT : launcher | cpu + temp | gpu | ram | disk | network
// CENTER : cava | clock | date | mpris
// RIGHT : wireplumber | backlight | bluetooth | idle_inhibitor |
// battery | power-profile | keyboard-state |
// systemd-failed | uptime | tray
// ────────────────────────────────────────────────────────────────────────
"layer": "top",
"position": "top",
"height": 60,
"margin-top": 0,
"margin-left": 16,
"margin-right": 16,
"spacing": 0,
"exclusive": true,
"modules-left": [
"custom/launcher",
"custom/sep",
"cpu",
"temperature",
"custom/gpu",
"memory",
"disk",
"custom/sep",
"custom/network"
],
"modules-center": [
"custom/cava",
"clock",
"custom/date",
"mpris"
],
"modules-right": [
"wireplumber",
"backlight",
"bluetooth",
"custom/sep",
"idle_inhibitor",
"custom/sep",
"battery",
"custom/power-profile",
"custom/sep",
"custom/uptime",
"tray",
"custom/power"
],
// ── Launcher ────────────────────────────────────────────────────────────
"custom/launcher": {
"format": "󰊠",
"tooltip": false,
"on-click": "~/.config/waybar/scripts/rofi-launcher.sh"
},
// ── Séparateur ──────────────────────────────────────────────────────────
"custom/sep": {
"format": "|",
"tooltip": false
},
// ── CPU ─────────────────────────────────────────────────────────────────
"cpu": {
"format": " {usage}%",
"tooltip-format": "<b>Intel i5-12450H</b>\n<span color='#8be9fd'> {usage}%</span> <span color='#6c7086'>@ {avg_frequency} GHz</span>\n\n<span color='#e79cfe'>T00</span> {usage0}% <span color='#e79cfe'>T01</span> {usage1}% <span color='#e79cfe'>T02</span> {usage2}% <span color='#e79cfe'>T03</span> {usage3}%\n<span color='#e79cfe'>T04</span> {usage4}% <span color='#e79cfe'>T05</span> {usage5}% <span color='#e79cfe'>T06</span> {usage6}% <span color='#e79cfe'>T07</span> {usage7}%\n<span color='#e79cfe'>T08</span> {usage8}% <span color='#e79cfe'>T09</span> {usage9}% <span color='#e79cfe'>T10</span> {usage10}% <span color='#e79cfe'>T11</span> {usage11}%",
"states": {
"warning": 70,
"critical": 90
},
"interval": 2
},
// ── Température ─────────────────────────────────────────────────────────
"temperature": {
"thermal-zone": 9,
"format": " {temperatureC}°",
"format-critical": " {temperatureC}°",
"critical-threshold": 80,
"tooltip": false,
"interval": 2
},
// ── GPU ─────────────────────────────────────────────────────────────────
"custom/gpu": {
"exec": "~/.config/waybar/scripts/gpu.sh",
"return-type": "json",
"interval": 2,
"format": "{}"
},
// ── RAM ─────────────────────────────────────────────────────────────────
"memory": {
"format": " {used:0.1f}G",
"tooltip-format": " RAM\n{used:0.1f} GiB / {total:0.1f} GiB\n{percentage}% utilisé",
"states": {
"warning": 75,
"critical": 90
},
"interval": 2
},
// ── Disque ──────────────────────────────────────────────────────────────
"disk": {
"format": "󰋊 {used}",
"tooltip-format": "󰋊 Disque /\n{used} / {total}\n{percentage_used}% utilisé",
"interval": 30
},
// ── Réseau ──────────────────────────────────────────────────────────────
"custom/network": {
"exec": "~/.config/waybar/scripts/network.sh",
"return-type": "json",
"interval": 2,
"format": "{}"
},
// ── CAVA ────────────────────────────────────────────────────────────────
"custom/cava": {
"exec": "~/.config/waybar/scripts/cava-read.sh",
"return-type": "json",
"interval": 1,
"format": "{}",
"tooltip": false
},
// ── Horloge ─────────────────────────────────────────────────────────────
"clock": {
"format": " {:%H:%M}",
"format-alt": " {:%H:%M:%S}",
"tooltip-format": "<big>{:%B %Y}</big>\n<tt><small>{calendar}</small></tt>",
"calendar": {
"mode": "month",
"on-scroll": 1,
"format": {
"months": "<span color='#ff79c6'><b>{}</b></span>",
"days": "<span color='#f8f8f2'>{}</span>",
"weeks": "<span color='#8be9fd'><b>W{}</b></span>",
"weekdays": "<span color='#e79cfe'><b>{}</b></span>",
"today": "<span color='#ff79c6'><b><u>{}</u></b></span>"
}
},
"interval": 1
},
// ── Date ────────────────────────────────────────────────────────────────
"custom/date": {
"exec": "LC_ALL=fr_FR.UTF-8 date '+%a %d %b' | awk '{print toupper(substr($0,1,1)) substr($0,2)}'",
"interval": 60,
"tooltip": false
},
// ── MPRIS ───────────────────────────────────────────────────────────────
"mpris": {
"format": "{player_icon} {dynamic}",
"format-paused": "{player_icon} <i>{dynamic}</i>",
"player-icons": {
"default": "󰎈",
"spotify": "󰓇",
"firefox": "󰈹",
"chromium": "󰊯",
"vlc": "󰕼"
},
"status-icons": {
"paused": "󰏤",
"playing": "󰐊",
"stopped": "󰐊"
},
"dynamic-len": 30,
"ignored-players": ["firefox"],
"tooltip-format": "{player} — {title}\n{artist}\n{album}"
},
// ── Volume (wireplumber) ─────────────────────────────────────────────────
"wireplumber": {
"format": "{icon} {volume}%",
"format-muted": "󰖁 {volume}%",
"format-icons": ["󰕿", "󰖀", "󰕾"],
"on-click": "python3 ~/.config/waybar/scripts/vc-media-popup.py",
"on-click-right": "~/.config/waybar/scripts/wob-volume.sh mute",
"on-scroll-up": "~/.config/waybar/scripts/wob-volume.sh up",
"on-scroll-down": "~/.config/waybar/scripts/wob-volume.sh down",
"tooltip-format": "󰕾 Volume : {volume}%\n{node_name}"
},
// ── Luminosité ──────────────────────────────────────────────────────────
"backlight": {
"format": "{icon} {percent}%",
"format-icons": ["󰃞", "󰃟", "󰃠"],
"tooltip": false,
"on-click": "python3 ~/.config/waybar/scripts/vc-media-popup.py",
"on-scroll-up": "~/.config/waybar/scripts/wob-brightness.sh up",
"on-scroll-down": "~/.config/waybar/scripts/wob-brightness.sh down"
},
// ── Bluetooth ───────────────────────────────────────────────────────────
"bluetooth": {
"format": "󰂯",
"format-disabled": "󰂲",
"format-connected": "󰂱 {device_alias}",
"tooltip-format": "{controller_alias} — {controller_address}\n{num_connections} connecté(s)",
"tooltip-format-connected": "{controller_alias}\n{device_enumerate}",
"tooltip-format-enumerate-connected": " {device_alias} ({device_address})"
},
// ── Idle inhibitor ──────────────────────────────────────────────────────
"idle_inhibitor": {
"format": "{icon}",
"format-icons": {
"activated": "󰅶",
"deactivated": "󰾪"
},
"tooltip-format-activated": "Veille désactivée",
"tooltip-format-deactivated": "Veille active"
},
// ── Batterie ────────────────────────────────────────────────────────────
"battery": {
"bat": "BAT1",
"states": {
"full": 95,
"good": 80,
"warning": 30,
"critical": 15
},
"format": "{icon} {capacity}%",
"format-charging": "󱐋 {capacity}%",
"format-plugged": "󰚥 {capacity}%",
"format-full": "󰁹 {capacity}%",
"format-icons": ["󰁺", "󰁻", "󰁼", "󰁽", "󰁾", "󰁿", "󰂀", "󰂁", "󰂂", "󰁹"],
"tooltip-format": "Batterie : {capacity}%\n{timeTo}\nCycles estimés",
"interval": 30
},
// ── Profil énergie ──────────────────────────────────────────────────────
"custom/power-profile": {
"exec": "~/.config/waybar/scripts/power-profile.sh",
"return-type": "json",
"interval": 5,
"signal": 8,
"on-click": "~/.config/waybar/scripts/power-profile.sh --toggle"
},
// ── Clavier ─────────────────────────────────────────────────────────────
"keyboard-state": {
"numlock": true,
"capslock": true,
"format": {
"numlock": "{icon}",
"capslock": "{icon}"
},
"format-icons": {
"locked": "",
"unlocked": ""
}
},
// ── Systemd failed ──────────────────────────────────────────────────────
"systemd-failed-units": {
"format": "󰚌 {nr_failed}",
"format-ok": "",
"hide-on-ok": true,
"system": true,
"user": true
},
// ── Uptime ──────────────────────────────────────────────────────────────
"custom/uptime": {
"exec": "awk '{s=$1; d=int(s/86400); h=int((s%86400)/3600); m=int((s%3600)/60); if(d>0) printf \"󰅐 %dj %dh\",d,h; else if(h>0) printf \"󰅐 %dh %dm\",h,m; else printf \"󰅐 %dm\",m}' /proc/uptime",
"interval": 60,
"tooltip": false
},
// ── Tray ────────────────────────────────────────────────────────────────
"tray": {
"spacing": 8,
"icon-size": 16
},
// ── Bouton power ────────────────────────────────────────────────────────
"custom/power": {
"format": "󰐥",
"tooltip": false,
"on-click": "~/.config/waybar/scripts/power-menu.sh"
}
}

View File

@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# cava-read.sh — lit /tmp/waybar_cava → JSON waybar (interval: 1)
# Lance automatiquement le démon cava-waybar.sh si absent.
OUT="/tmp/waybar_cava"
PID="/tmp/waybar_cava.pid"
DAEMON="$HOME/.config/waybar/scripts/cava-waybar.sh"
# Démarrer le démon si pas actif
if [[ ! -f "$PID" ]] || ! kill -0 "$(cat "$PID" 2>/dev/null)" 2>/dev/null; then
nohup bash "$DAEMON" >/dev/null 2>&1 &
fi
# Lire et retourner le JSON
if [[ -f "$OUT" ]]; then
BAR=$(tail -1 "$OUT" 2>/dev/null)
[[ -n "$BAR" ]] && printf '{"text":"%s","tooltip":"","class":""}\n' "$BAR" && exit 0
fi
printf '{"text":"▁▁▁▁▁▁▁▁","tooltip":"","class":"muted"}\n'

View File

@@ -0,0 +1,12 @@
#!/usr/bin/env bash
# cava-waybar-daemon.sh — lance CAVA en daemon → /tmp/waybar_cava
# Appeler au démarrage de la session
pkill -f "cava -p.*cava-waybar.cfg" 2>/dev/null
sleep 0.3
CFG="$HOME/.config/waybar/cava-waybar.cfg"
[[ ! -f "$CFG" ]] && { echo "Config manquante : $CFG"; exit 1; }
cava -p "$CFG" > /tmp/waybar_cava &
echo "CAVA daemon lancé (PID $!)"

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env bash
# cava-waybar.sh — démon CAVA : écrit la visu dans /tmp/waybar_cava
# Lancé automatiquement par cava-read.sh, ne pas appeler directement.
BLOCKS=('▁' '▁' '▂' '▃' '▄' '▅' '▆' '▇' '█')
CFG="$HOME/.config/waybar/cava-waybar.cfg"
OUT="/tmp/waybar_cava"
PID="/tmp/waybar_cava.pid"
echo $$ > "$PID"
cleanup() { rm -f "$PID" "$OUT"; exit; }
trap cleanup EXIT INT TERM
cava -p "$CFG" | while IFS=';' read -ra VALUES; do
BAR=""
for v in "${VALUES[@]}"; do
v="${v//[^0-9]/}"
[[ -n "$v" ]] && BAR+="${BLOCKS[$v]:-}"
done
[[ -n "$BAR" ]] && printf '%s\n' "$BAR" > "$OUT"
done

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env bash
# gpu.sh — NVIDIA GPU stats → JSON waybar
DATA=$(nvidia-smi --query-gpu=utilization.gpu,temperature.gpu,memory.used,memory.total \
--format=csv,noheader,nounits 2>/dev/null)
if [[ -z "$DATA" ]]; then
echo '{"text":"","tooltip":"GPU non disponible","class":"","percentage":0}'
exit 0
fi
LOAD=$(echo "$DATA" | awk -F', ' '{print $1}')
TEMP=$(echo "$DATA" | awk -F', ' '{print $2}')
MEM_USED=$(echo "$DATA" | awk -F', ' '{print $3}')
MEM_TOTAL=$(echo "$DATA" | awk -F', ' '{print $4}')
if (( LOAD > 90 )); then CLASS="critical"
elif (( LOAD > 70 )); then CLASS="warning"
else CLASS="normal"
fi
TEXT="󰢮 ${LOAD}% ${TEMP}°"
TOOLTIP="󰢮 GPU\nCharge : ${LOAD}%\nTempérature : ${TEMP}°C\nVRAM : ${MEM_USED} / ${MEM_TOTAL} MiB"
printf '{"text":"%s","tooltip":"%s","class":"%s","percentage":%s}\n' \
"$TEXT" "$TOOLTIP" "$CLASS" "$LOAD"

View File

@@ -0,0 +1,66 @@
#!/usr/bin/env bash
# network.sh — bande passante réseau → JSON waybar
STATE_FILE="/tmp/waybar_net_state"
# Détecter l'interface active
IFACE=""
for candidate in enp7s0 enp6s0 eth0; do
if [[ -d "/sys/class/net/$candidate" && "$(cat /sys/class/net/$candidate/operstate 2>/dev/null)" == "up" ]]; then
IFACE="$candidate"; TYPE="eth"; break
fi
done
if [[ -z "$IFACE" ]]; then
for candidate in wlp8s0 wlp0s20f3 wlan0; do
if [[ -d "/sys/class/net/$candidate" && "$(cat /sys/class/net/$candidate/operstate 2>/dev/null)" == "up" ]]; then
IFACE="$candidate"; TYPE="wifi"; break
fi
done
fi
if [[ -z "$IFACE" ]]; then
echo '{"text":"󰤭 déco","tooltip":"Déconnecté","class":"disconnected"}'
exit 0
fi
RX_NOW=$(cat "/sys/class/net/$IFACE/statistics/rx_bytes" 2>/dev/null || echo 0)
TX_NOW=$(cat "/sys/class/net/$IFACE/statistics/tx_bytes" 2>/dev/null || echo 0)
NOW=$(date +%s%N)
if [[ -f "$STATE_FILE" ]]; then
read -r RX_PREV TX_PREV TIME_PREV < "$STATE_FILE"
ELAPSED=$(( (NOW - TIME_PREV) / 1000000 )) # ms
if (( ELAPSED > 0 )); then
DOWN_BPS=$(( (RX_NOW - RX_PREV) * 1000 / ELAPSED ))
UP_BPS=$(( (TX_NOW - TX_PREV) * 1000 / ELAPSED ))
else
DOWN_BPS=0; UP_BPS=0
fi
else
DOWN_BPS=0; UP_BPS=0
fi
echo "$RX_NOW $TX_NOW $NOW" > "$STATE_FILE"
# Formatage humain
human() {
local B=$1
if (( B >= 1073741824 )); then LC_ALL=C awk "BEGIN{printf \"%.1fG\", $B/1073741824}"
elif (( B >= 1048576 )); then LC_ALL=C awk "BEGIN{printf \"%.1fM\", $B/1048576}"
elif (( B >= 1024 )); then LC_ALL=C awk "BEGIN{printf \"%.0fK\", $B/1024}"
else echo "${B}B"
fi
}
DOWN_H=$(human $DOWN_BPS)
UP_H=$(human $UP_BPS)
RX_TOTAL=$(human $RX_NOW)
TX_TOTAL=$(human $TX_NOW)
ICON_DOWN="󰇚"; ICON_UP="󰕒"
[[ "$TYPE" == "wifi" ]] && ICON_NET="󰤨" || ICON_NET="󰈀"
TEXT="${ICON_DOWN} ${DOWN_H}/s ${ICON_UP} ${UP_H}/s"
TOOLTIP="${ICON_NET} ${IFACE}\n${ICON_DOWN} ${DOWN_H}/s ${ICON_UP} ${UP_H}/s\n\n󰇚 Total reçu : ${RX_TOTAL}\n󰕒 Total envoyé : ${TX_TOTAL}"
printf '{"text":"%s","tooltip":"%s","class":"%s"}\n' "$TEXT" "$TOOLTIP" "$TYPE"

View File

@@ -0,0 +1,27 @@
#!/usr/bin/env bash
# power-menu.sh — menu power dédié (wofi)
STYLE="$HOME/.config/wofi/power-style.css"
ENTRIES=(
"󰌾 Verrouiller"
"󰒲 Veille"
"󰑓 Redémarrer"
"󰐥 Éteindre"
)
CHOICE=$(printf '%s\n' "${ENTRIES[@]}" | \
wofi --dmenu \
--prompt "⏻ " \
--style "$STYLE" \
--width 210 \
--height 160 \
--y 70 \
--location top_right)
case "$CHOICE" in
"󰌾 Verrouiller") loginctl lock-session ;;
"󰒲 Veille") systemctl suspend ;;
"󰑓 Redémarrer") systemctl reboot ;;
"󰐥 Éteindre") systemctl poweroff ;;
esac

View File

@@ -0,0 +1,53 @@
#!/usr/bin/env bash
# power-profile.sh — profil énergie ACPI → JSON waybar
# Sans argument : affiche le profil courant en JSON
# --toggle : cycle vers le profil suivant + applique la luminosité
# Luminosité par profil (%)
BRIGHT_performance=80
BRIGHT_balanced=60
BRIGHT_low_power=30 # low-power → clé sans tiret
_bright_for() {
local key="${1//-/_}"
local var="BRIGHT_${key}"
echo "${!var:-60}"
}
if [[ "$1" == "--toggle" ]]; then
CURRENT=$(cat /sys/firmware/acpi/platform_profile 2>/dev/null || echo "balanced")
case "$CURRENT" in
performance) NEXT="balanced" ;;
balanced) NEXT="low-power" ;;
*) NEXT="performance" ;;
esac
echo "$NEXT" > /sys/firmware/acpi/platform_profile
# Appliquer la luminosité du nouveau profil
BRIGHT=$(_bright_for "$NEXT")
brightnessctl set "${BRIGHT}%" -q
# Feedback overlay
if [[ -p /tmp/wob.fifo ]]; then
echo "b:${BRIGHT}" > /tmp/wob.fifo 2>/dev/null || true
fi
# Rafraîchir le module waybar
pkill -RTMIN+8 waybar
exit 0
fi
# ── Affichage JSON ────────────────────────────────────────────────────────────
PROFILE=$(cat /sys/firmware/acpi/platform_profile 2>/dev/null || echo "unknown")
case "$PROFILE" in
performance) ICON="󱐋"; CLASS="performance" ;;
balanced) ICON="󰾅"; CLASS="balanced" ;;
low-power) ICON="󰌪"; CLASS="low-power" ;;
*) ICON="󰒓"; CLASS="unknown" ;;
esac
printf '{"text":"%s","tooltip":"Profil énergie : %s","class":"%s"}\n' \
"$ICON" "$PROFILE" "$CLASS"

View File

@@ -0,0 +1,116 @@
#!/usr/bin/env bash
# launcher.sh — boite à outils violet-chaton avec historique (wofi)
STYLE="$HOME/.config/wofi/style.css"
TERM="cosmic-term"
HIST_FILE="$HOME/.cache/waybar-launcher.hist"
MAX_HIST=5
HIST_ICON="󰄴 "
SEP="<span foreground='#3d2454'>────────────────────</span>"
HIST_HDR="<span foreground='#8be9fd' size='small'> RÉCENTS</span>"
# ── Favoris (ordre fixe, toujours en haut) ───────────────────────────────────
FAVORITES=(
"󰖟 Vivaldi"
"󰆍 Terminal"
"󰉋 Nemo"
"󰨞 VSCode"
"󰙯 Vesktop"
"󱑤 btop"
"󰊢 lazygit"
"󰘳 pipes.sh"
"󱒕 cbonsai"
)
# ── Historique (dernières applis lancées, dédoublonné) ───────────────────────
RECENT_ENTRIES=""
if [[ -f "$HIST_FILE" ]]; then
RECENT_ENTRIES=$(awk '!seen[$0]++' "$HIST_FILE" | head -"$MAX_HIST" | \
sed "s|^|${HIST_ICON}|")
fi
# ── Toutes les applis installées ─────────────────────────────────────────────
ALL_APPS=$(for f in \
/usr/share/applications/*.desktop \
~/.local/share/applications/*.desktop \
/var/lib/flatpak/exports/share/applications/*.desktop \
~/.local/share/flatpak/exports/share/applications/*.desktop; do
[[ -f "$f" ]] || continue
grep -q "^NoDisplay=true" "$f" && continue
grep -q "^Type=Application" "$f" || continue
grep -m1 "^Name=" "$f" | cut -d= -f2-
done | sort -u)
# ── Construction de la liste ─────────────────────────────────────────────────
FULL_LIST=$(
printf '%s\n' "${FAVORITES[@]}"
if [[ -n "$RECENT_ENTRIES" ]]; then
echo "$SEP"
echo "$HIST_HDR"
echo "$RECENT_ENTRIES"
fi
echo "$SEP"
echo "$ALL_APPS"
)
# ── Affichage wofi ───────────────────────────────────────────────────────────
CHOICE=$(echo "$FULL_LIST" | \
wofi --dmenu \
--prompt " " \
--style "$STYLE" \
--width 300 \
--height 500 \
--x 16 \
--y 70 \
--location top_left \
--cache-file=/dev/null)
[[ -z "$CHOICE" ]] && exit 0
# Enlever le préfixe historique si présent
CHOICE_CLEAN="${CHOICE#$HIST_ICON}"
# ── Mise à jour historique ────────────────────────────────────────────────────
save_history() {
local app="$1"
# Ignorer les favoris (déjà toujours visibles), séparateurs et power
case "$app" in
"󰖟 Vivaldi"|"󰆍 Terminal"|"󰉋 Nemo"|"󰨞 VSCode"|"󰙯 Vesktop") return ;;
"󱑤 btop"|"󰊢 lazygit"|"󰘳 pipes.sh"|"󱒕 cbonsai") return ;;
*"────"*|*"RÉCENTS"*|"") return ;;
esac
# Dépiler l'entrée existante et rajouter en tête
local tmp
tmp=$(grep -vxF "$app" "$HIST_FILE" 2>/dev/null)
printf '%s\n%s\n' "$app" "$tmp" | head -20 > "$HIST_FILE"
}
# ── Actions ───────────────────────────────────────────────────────────────────
case "$CHOICE_CLEAN" in
"󰖟 Vivaldi") vivaldi-stable & ;;
"󰆍 Terminal") $TERM & ;;
"󰉋 Nemo") nemo & ;;
"󰨞 VSCode") code & ;;
"󰙯 Vesktop") flatpak run dev.vencord.Vesktop & ;;
"󱑤 btop") $TERM --command btop & ;;
"󰊢 lazygit") $TERM --command lazygit & ;;
"󰘳 pipes.sh") $TERM --command pipes.sh & ;;
"󱒕 cbonsai") $TERM --command bash -c "cbonsai -l; read" & ;;
*"────"*|*"RÉCENTS"*) : ;;
*)
save_history "$CHOICE_CLEAN"
DESKTOP=$(grep -rlm1 "^Name=$CHOICE_CLEAN$" \
/usr/share/applications \
~/.local/share/applications \
/var/lib/flatpak/exports/share/applications \
~/.local/share/flatpak/exports/share/applications \
2>/dev/null | head -1)
if [[ -n "$DESKTOP" ]]; then
# Lancer via Exec= directement — évite gtk-launch qui enregistre
# l'app dans GIO/recently-used et déclenche l'ajout aux favoris COSMIC
EXEC=$(grep -m1 "^Exec=" "$DESKTOP" | cut -d= -f2- | sed 's/ *%[a-zA-Z]//g' | xargs)
[[ -n "$EXEC" ]] && setsid bash -c "$EXEC" &
fi
;;
esac

View File

@@ -0,0 +1,245 @@
#!/usr/bin/env python3
# vc-brightness-popup.py — Popup luminosité violet-chaton
# Lancé par le clic sur le module backlight de waybar
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GtkLayerShell', '0.1')
from gi.repository import Gtk, Gdk, GtkLayerShell, GLib
import subprocess
import os
import re
# ── CSS ───────────────────────────────────────────────────────────────────────
CSS = b"""
window {
background-color: rgba(52, 28, 74, 0.93);
border: 3px solid rgba(255, 121, 198, 0.78);
border-radius: 14px;
}
#container {
padding: 14px 20px 16px 20px;
}
#bright-icon {
color: #8be9fd;
font-family: "JetBrainsMono Nerd Font";
font-size: 18px;
min-width: 24px;
}
#bright-title {
color: rgba(248, 248, 242, 0.55);
font-family: "JetBrainsMono Nerd Font";
font-size: 11px;
}
#device-name {
color: #8be9fd;
font-family: "JetBrainsMono Nerd Font";
font-size: 11px;
font-weight: bold;
}
#bright-pct {
color: #f8f8f2;
font-family: "JetBrainsMono Nerd Font";
font-size: 13px;
font-weight: bold;
min-width: 44px;
}
#separator {
color: rgba(92, 73, 108, 0.60);
margin: 4px 0;
}
scale trough {
background-color: rgba(92, 73, 108, 0.55);
border-radius: 3px;
min-height: 6px;
border: none;
}
scale highlight {
background-color: #8be9fd;
border-radius: 3px;
border: none;
}
scale slider {
background-color: #f8f8f2;
border-radius: 50%;
min-width: 18px;
min-height: 18px;
border: 2px solid rgba(139, 233, 253, 0.80);
box-shadow: none;
transition: none;
}
scale slider:hover {
background-color: #8be9fd;
border-color: #8be9fd;
}
"""
POPUP_WIDTH = 300
# ── Brightness helpers ────────────────────────────────────────────────────────
def get_brightness():
"""Retourne (valeur 0-100, nom du device)."""
try:
r = subprocess.run(
['brightnessctl', 'info'],
capture_output=True, text=True, timeout=2
)
pct_match = re.search(r'\((\d+)%\)', r.stdout)
dev_match = re.search(r"Device '([^']+)'", r.stdout)
pct = int(pct_match.group(1)) if pct_match else 50
dev = dev_match.group(1) if dev_match else 'Écran'
# Rendre le nom plus lisible
dev = dev.replace('_', ' ').replace('backlight', '').strip().title()
return pct, dev
except Exception:
return 50, 'Écran'
def set_brightness(pct):
pct = max(1, min(100, pct)) # minimum 1% pour ne pas éteindre l'écran
subprocess.run(
['brightnessctl', 'set', f'{pct}%', '-q'],
capture_output=True
)
# Feedback wob
fifo = '/tmp/wob.fifo'
if os.path.exists(fifo):
try:
fd = os.open(fifo, os.O_WRONLY | os.O_NONBLOCK)
os.write(fd, f'{pct}\n'.encode())
os.close(fd)
except OSError:
pass
def bright_icon(pct):
if pct < 34:
return '󰃞'
if pct < 67:
return '󰃟'
return '󰃠'
# ── Popup ─────────────────────────────────────────────────────────────────────
class BrightnessPopup(Gtk.Window):
def __init__(self):
super().__init__()
self._blocked = False
# ── Position : centré sous le module backlight ────────────────────────
display = Gdk.Display.get_default()
monitor = display.get_primary_monitor() if display else None
screen_w = monitor.get_geometry().width if monitor else 1920
# Backlight est le 2e module de la pill droite (~250px depuis le bord)
module_center = screen_w - 16 - 250
margin_left = max(0, module_center - POPUP_WIDTH // 2)
GtkLayerShell.init_for_window(self)
GtkLayerShell.set_layer(self, GtkLayerShell.Layer.OVERLAY)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.TOP, True)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.LEFT, True)
GtkLayerShell.set_margin(self, GtkLayerShell.Edge.TOP, 66)
GtkLayerShell.set_margin(self, GtkLayerShell.Edge.LEFT, margin_left)
GtkLayerShell.set_keyboard_mode(self, GtkLayerShell.KeyboardMode.ON_DEMAND)
GtkLayerShell.set_exclusive_zone(self, -1)
self.set_decorated(False)
self.set_resizable(False)
self.set_default_size(POPUP_WIDTH, -1)
# ── CSS ───────────────────────────────────────────────────────────────
provider = Gtk.CssProvider()
provider.load_from_data(CSS)
Gtk.StyleContext.add_provider_for_screen(
Gdk.Screen.get_default(),
provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)
# ── État initial ──────────────────────────────────────────────────────
pct, dev = get_brightness()
# ── Layout ────────────────────────────────────────────────────────────
container = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
container.set_name('container')
self.add(container)
# Ligne device
dev_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
dev_icon = Gtk.Label(label='󰍹')
dev_icon.set_name('bright-title')
dev_row.pack_start(dev_icon, False, False, 0)
dev_label = Gtk.Label(label=dev)
dev_label.set_name('device-name')
dev_label.set_halign(Gtk.Align.START)
dev_label.set_ellipsize(3)
dev_row.pack_start(dev_label, True, True, 0)
container.pack_start(dev_row, False, False, 0)
# Séparateur
sep = Gtk.Label(label='' * 30)
sep.set_name('separator')
container.pack_start(sep, False, False, 4)
# En-tête : icône + "Luminosité" + %
header = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
self.icon = Gtk.Label(label=bright_icon(pct))
self.icon.set_name('bright-icon')
header.pack_start(self.icon, False, False, 0)
title = Gtk.Label(label='Luminosité')
title.set_name('bright-title')
title.set_halign(Gtk.Align.START)
header.pack_start(title, True, True, 0)
self.pct = Gtk.Label(label=f'{pct}%')
self.pct.set_name('bright-pct')
self.pct.set_halign(Gtk.Align.END)
header.pack_end(self.pct, False, False, 0)
container.pack_start(header, False, False, 0)
# Slider (min 1% pour ne pas éteindre l'écran)
self.scale = Gtk.Scale.new_with_range(
Gtk.Orientation.HORIZONTAL, 1, 100, 5
)
self.scale.set_value(pct)
self.scale.set_draw_value(False)
self.scale.set_hexpand(True)
self.scale.connect('value-changed', self._on_changed)
container.pack_start(self.scale, False, False, 0)
# ── Fermeture ─────────────────────────────────────────────────────────
self.connect('key-press-event', self._on_key)
self.connect('focus-out-event', lambda *_: self.destroy())
self.show_all()
self.grab_focus()
def _on_changed(self, scale):
if self._blocked:
return
pct = int(scale.get_value())
self.pct.set_label(f'{pct}%')
self.icon.set_label(bright_icon(pct))
set_brightness(pct)
def _on_key(self, _widget, event):
if event.keyval == Gdk.KEY_Escape:
self.destroy()
if __name__ == '__main__':
win = BrightnessPopup()
win.connect('destroy', Gtk.main_quit)
Gtk.main()

View File

@@ -0,0 +1,498 @@
#!/usr/bin/env python3
# vc-media-popup.py — Popup audio + luminosité violet-chaton
# Lancé depuis le clic sur wireplumber OU backlight
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GtkLayerShell', '0.1')
from gi.repository import Gtk, Gdk, GtkLayerShell, GLib
import subprocess
import os
import re
# ── CSS ───────────────────────────────────────────────────────────────────────
CSS = """
window {
background-color: rgba(52, 28, 74, 0.93);
border: 3px solid rgba(255, 121, 198, 0.78);
border-radius: 14px;
}
#container {
padding: 14px 20px 16px 20px;
}
/* ── Labels ────────────────────────────────────────────────────────────────── */
#section-label {
color: #ff79c6;
font-family: "JetBrainsMono Nerd Font";
font-size: 10px;
font-weight: bold;
letter-spacing: 0.08em;
}
#device-name {
color: rgba(248, 248, 242, 0.75);
font-family: "JetBrainsMono Nerd Font";
font-size: 11px;
}
#pct-label {
color: #f8f8f2;
font-family: "JetBrainsMono Nerd Font";
font-size: 12px;
font-weight: bold;
min-width: 38px;
}
#separator {
color: rgba(92, 73, 108, 0.50);
margin: 6px 0 4px 0;
}
/* ── Boutons mute (icônes cliquables) ───────────────────────────────────────── */
#mute-icon {
font-family: "JetBrainsMono Nerd Font";
font-size: 17px;
color: #ff79c6;
background: transparent;
border: none;
border-radius: 6px;
padding: 0 4px;
min-width: 28px;
}
#mute-icon:hover {
background: rgba(255, 121, 198, 0.15);
}
#mute-icon.muted {
color: rgba(243, 139, 168, 0.70);
}
#mic-icon {
font-family: "JetBrainsMono Nerd Font";
font-size: 17px;
color: #ff79c6;
background: transparent;
border: none;
border-radius: 6px;
padding: 0 4px;
min-width: 28px;
}
#mic-icon:hover {
background: rgba(255, 121, 198, 0.15);
}
#mic-icon.muted {
color: rgba(243, 139, 168, 0.70);
}
/* ── Sliders audio (rose) ───────────────────────────────────────────────────── */
scale.audio {
min-height: 22px;
}
scale.audio trough {
background-color: rgba(92, 73, 108, 0.55);
border-radius: 3px;
min-height: 5px;
border: none;
}
scale.audio trough highlight {
background-color: #ff79c6;
min-height: 6px;
border-radius: 3px;
border: none;
}
scale.audio.muted trough highlight {
background-color: rgba(108, 112, 134, 0.40);
}
scale.audio slider {
background-color: #f8f8f2;
border-radius: 50%;
min-width: 16px;
min-height: 16px;
border: 2px solid rgba(255, 121, 198, 0.80);
box-shadow: none;
transition: none;
}
scale.audio slider:hover {
background-color: #e79cfe;
border-color: #ff79c6;
}
scale.audio.muted slider {
border-color: rgba(108, 112, 134, 0.60);
}
/* ── Slider luminosité (cyan) ───────────────────────────────────────────────── */
scale.bright {
min-height: 22px;
}
scale.bright trough {
background-color: rgba(92, 73, 108, 0.55);
border-radius: 3px;
min-height: 5px;
border: none;
}
scale.bright trough highlight {
background-color: #8be9fd;
min-height: 6px;
border-radius: 3px;
border: none;
}
scale.bright slider {
background-color: #f8f8f2;
border-radius: 50%;
min-width: 16px;
min-height: 16px;
border: 2px solid rgba(139, 233, 253, 0.80);
box-shadow: none;
transition: none;
}
scale.bright slider:hover {
background-color: #8be9fd;
border-color: #8be9fd;
}
#bright-icon {
color: #8be9fd;
font-family: "JetBrainsMono Nerd Font";
font-size: 17px;
min-width: 28px;
}
"""
POPUP_WIDTH = 310
# ── Helpers ───────────────────────────────────────────────────────────────────
def run(cmd, **kw):
return subprocess.run(cmd, capture_output=True, text=True, timeout=2, **kw)
def get_sink_volume():
r = run(['wpctl', 'get-volume', '@DEFAULT_AUDIO_SINK@'])
parts = r.stdout.strip().split()
vol = int(float(parts[1]) * 100) if len(parts) >= 2 else 50
return min(max(vol, 0), 100), '[MUTED]' in r.stdout
def get_source_volume():
r = run(['wpctl', 'get-volume', '@DEFAULT_AUDIO_SOURCE@'])
parts = r.stdout.strip().split()
vol = int(float(parts[1]) * 100) if len(parts) >= 2 else 50
return min(max(vol, 0), 100), '[MUTED]' in r.stdout
def get_node_name(target):
r = run(['wpctl', 'inspect', target])
for field in ('node.description', 'node.nick'):
m = re.search(rf'{field}\s*=\s*"([^"]+)"', r.stdout)
if m:
return m.group(1)
return target
def set_sink_vol(v):
run(['wpctl', 'set-volume', '-l', '1.0', '@DEFAULT_AUDIO_SINK@', f'{v}%'])
_wob(f'v:{v}')
def set_source_vol(v):
run(['wpctl', 'set-volume', '@DEFAULT_AUDIO_SOURCE@', f'{v}%'])
_wob(f'v:{v}')
def toggle_sink_mute():
run(['wpctl', 'set-mute', '@DEFAULT_AUDIO_SINK@', 'toggle'])
def toggle_source_mute():
run(['wpctl', 'set-mute', '@DEFAULT_AUDIO_SOURCE@', 'toggle'])
def get_brightness():
r = run(['brightnessctl', 'info'])
m = re.search(r'\((\d+)%\)', r.stdout)
return int(m.group(1)) if m else 50
def set_brightness(v):
v = max(1, v)
run(['brightnessctl', 'set', f'{v}%', '-q'])
_wob(f'b:{v}')
def _wob(msg):
fifo = '/tmp/wob.fifo'
if os.path.exists(fifo):
try:
fd = os.open(fifo, os.O_WRONLY | os.O_NONBLOCK)
os.write(fd, f'{msg}\n'.encode())
os.close(fd)
except OSError:
pass
def vol_icon(muted):
return '󰖁' if muted else '󰕾'
def mic_icon(muted):
return '󰍭' if muted else '󰍬'
def bright_icon(pct):
if pct < 34: return '󰃞'
if pct < 67: return '󰃟'
return '󰃠'
# ── Popup ─────────────────────────────────────────────────────────────────────
class MediaPopup(Gtk.Window):
def __init__(self):
super().__init__()
self._blk = False
# ── Position ─────────────────────────────────────────────────────────
display = Gdk.Display.get_default()
monitor = display.get_primary_monitor() if display else None
screen_w = monitor.get_geometry().width if monitor else 1920
module_center = screen_w - 16 - 210
margin_left = max(0, module_center - POPUP_WIDTH // 2)
GtkLayerShell.init_for_window(self)
GtkLayerShell.set_layer(self, GtkLayerShell.Layer.OVERLAY)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.TOP, True)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.LEFT, True)
GtkLayerShell.set_margin(self, GtkLayerShell.Edge.TOP, 66)
GtkLayerShell.set_margin(self, GtkLayerShell.Edge.LEFT, margin_left)
GtkLayerShell.set_keyboard_mode(self, GtkLayerShell.KeyboardMode.ON_DEMAND)
GtkLayerShell.set_exclusive_zone(self, -1)
self.set_decorated(False)
self.set_resizable(False)
self.set_default_size(POPUP_WIDTH, -1)
# ── CSS ───────────────────────────────────────────────────────────────
provider = Gtk.CssProvider()
provider.load_from_data(CSS.encode())
Gtk.StyleContext.add_provider_for_screen(
Gdk.Screen.get_default(), provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)
# ── États initiaux ────────────────────────────────────────────────────
sink_vol, sink_muted = get_sink_volume()
src_vol, src_muted = get_source_volume()
self._sink_muted = sink_muted
self._src_muted = src_muted
# ── Layout ────────────────────────────────────────────────────────────
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
box.set_name('container')
self.add(box)
# ╔═══ SORTIE ══════════════════════════════════════════════════════════╗
box.pack_start(self._section_header('SORTIE', '󰕾'), False, False, 0)
box.pack_start(self._device_label(
get_node_name('@DEFAULT_AUDIO_SINK@')), False, False, 2)
sink_row, self.sink_scale, self.sink_pct, self.sink_icon = \
self._slider_row(sink_vol, sink_muted, 'audio', vol_icon(sink_muted),
self._toggle_sink_mute, '@DEFAULT_AUDIO_SINK@')
box.pack_start(sink_row, False, False, 4)
# ╔═══ ENTRÉE ══════════════════════════════════════════════════════════╗
sep1 = Gtk.Label(label='' * 34)
sep1.set_name('separator')
box.pack_start(sep1, False, False, 0)
box.pack_start(self._section_header('ENTRÉE', '󰍬'), False, False, 0)
box.pack_start(self._device_label(
get_node_name('@DEFAULT_AUDIO_SOURCE@')), False, False, 2)
src_row, self.src_scale, self.src_pct, self.src_icon = \
self._slider_row(src_vol, src_muted, 'audio', mic_icon(src_muted),
self._toggle_src_mute, '@DEFAULT_AUDIO_SOURCE@')
box.pack_start(src_row, False, False, 4)
# ╔═══ LUMINOSITÉ ══════════════════════════════════════════════════════╗
sep2 = Gtk.Label(label='' * 34)
sep2.set_name('separator')
box.pack_start(sep2, False, False, 0)
bright_row, self.bright_scale, self.bright_pct, self.bright_icon_lbl = \
self._bright_row(get_brightness())
box.pack_start(bright_row, False, False, 6)
# ── Refresh + fermeture ───────────────────────────────────────────────
GLib.timeout_add(2000, self._refresh)
self.connect('key-press-event', lambda w, e:
self.destroy() if e.keyval == Gdk.KEY_Escape else None)
self.connect('focus-out-event', lambda *_: self.destroy())
self.show_all()
# GTK3 bug : set_value() avant réalisation → highlight width=0 à max
# Forcer un re-calcul après que les widgets sont visibles
GLib.idle_add(self._redraw_scales)
self.grab_focus()
def _redraw_scales(self):
"""Force GTK3 à recalculer les highlights.
set_value(même_valeur) est un no-op — on oscille ±1 pour déclencher
un vrai recalcul de la position du highlight dans le trough."""
self._blk = True
for scale in [self.sink_scale, self.src_scale, self.bright_scale]:
v = scale.get_value()
adj = scale.get_adjustment()
lo = adj.get_lower()
scale.set_value(max(lo, v - 1)) # valeur différente → GTK recalcule
scale.set_value(v) # retour à la valeur réelle
self._blk = False
return False
# ── Builders UI ───────────────────────────────────────────────────────────
def _section_header(self, label, icon):
row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
ico = Gtk.Label(label=icon)
ico.set_name('section-label')
row.pack_start(ico, False, False, 0)
lbl = Gtk.Label(label=label)
lbl.set_name('section-label')
lbl.set_halign(Gtk.Align.START)
row.pack_start(lbl, True, True, 0)
return row
def _device_label(self, name):
lbl = Gtk.Label(label=name)
lbl.set_name('device-name')
lbl.set_halign(Gtk.Align.START)
lbl.set_ellipsize(3)
lbl.set_margin_start(4)
return lbl
def _slider_row(self, val, muted, css_class, icon_char, mute_cb, target):
"""Retourne (row, scale, pct_label, icon_btn)"""
row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
# Icône = bouton mute
icon_btn = Gtk.Button(label=icon_char)
icon_btn.set_name('mute-icon' if target == '@DEFAULT_AUDIO_SINK@' else 'mic-icon')
if muted:
icon_btn.get_style_context().add_class('muted')
icon_btn.connect('clicked', mute_cb)
row.pack_start(icon_btn, False, False, 0)
# Slider
scale = Gtk.Scale.new_with_range(Gtk.Orientation.HORIZONTAL, 0, 100, 5)
scale.set_value(val)
scale.set_draw_value(False)
scale.set_hexpand(True)
scale.get_style_context().add_class(css_class)
if muted:
scale.get_style_context().add_class('muted')
scale.connect('value-changed', lambda s, t=target:
self._on_audio_changed(s, pct_lbl, t))
row.pack_start(scale, True, True, 0)
# %
pct_lbl = Gtk.Label(label=f'{val}%')
pct_lbl.set_name('pct-label')
pct_lbl.set_halign(Gtk.Align.END)
row.pack_end(pct_lbl, False, False, 0)
return row, scale, pct_lbl, icon_btn
def _bright_row(self, pct):
row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
icon_lbl = Gtk.Label(label=bright_icon(pct))
icon_lbl.set_name('bright-icon')
row.pack_start(icon_lbl, False, False, 0)
scale = Gtk.Scale.new_with_range(Gtk.Orientation.HORIZONTAL, 1, 100, 5)
scale.set_value(pct)
scale.set_draw_value(False)
scale.set_hexpand(True)
scale.get_style_context().add_class('bright')
scale.connect('value-changed', self._on_bright_changed)
row.pack_start(scale, True, True, 0)
pct_lbl = Gtk.Label(label=f'{pct}%')
pct_lbl.set_name('pct-label')
pct_lbl.set_halign(Gtk.Align.END)
row.pack_end(pct_lbl, False, False, 0)
return row, scale, pct_lbl, icon_lbl
# ── Handlers ──────────────────────────────────────────────────────────────
def _on_audio_changed(self, scale, pct_lbl, target):
if self._blk:
return
v = int(scale.get_value())
pct_lbl.set_label(f'{v}%')
if target == '@DEFAULT_AUDIO_SINK@':
set_sink_vol(v)
else:
set_source_vol(v)
def _on_bright_changed(self, scale):
if self._blk:
return
v = int(scale.get_value())
self.bright_pct.set_label(f'{v}%')
self.bright_icon_lbl.set_label(bright_icon(v))
set_brightness(v)
def _apply_mute(self, scale, icon_btn, muted, icon_fn):
sc = scale.get_style_context()
bc = icon_btn.get_style_context()
if muted:
sc.add_class('muted')
bc.add_class('muted')
else:
sc.remove_class('muted')
bc.remove_class('muted')
icon_btn.set_label(icon_fn(muted))
def _toggle_sink_mute(self, _btn):
toggle_sink_mute()
_, self._sink_muted = get_sink_volume()
self._apply_mute(self.sink_scale, self.sink_icon,
self._sink_muted, vol_icon)
def _toggle_src_mute(self, _btn):
toggle_source_mute()
_, self._src_muted = get_source_volume()
self._apply_mute(self.src_scale, self.src_icon,
self._src_muted, mic_icon)
def _refresh(self):
sink_vol, sink_muted = get_sink_volume()
src_vol, src_muted = get_source_volume()
if sink_muted != self._sink_muted:
self._sink_muted = sink_muted
self._apply_mute(self.sink_scale, self.sink_icon,
sink_muted, vol_icon)
if src_muted != self._src_muted:
self._src_muted = src_muted
self._apply_mute(self.src_scale, self.src_icon,
src_muted, mic_icon)
self._blk = True
self.sink_scale.set_value(sink_vol)
self.sink_pct.set_label(f'{sink_vol}%')
self.src_scale.set_value(src_vol)
self.src_pct.set_label(f'{src_vol}%')
self._blk = False
return True
if __name__ == '__main__':
win = MediaPopup()
win.connect('destroy', Gtk.main_quit)
Gtk.main()

View File

@@ -0,0 +1,422 @@
#!/usr/bin/env python3
# vc-volume-popup.py — Popup volume slider violet-chaton
# Lancé par le clic sur le module wireplumber de waybar
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GtkLayerShell', '0.1')
from gi.repository import Gtk, Gdk, GtkLayerShell, GLib
import subprocess
import os
import re
# ── CSS ───────────────────────────────────────────────────────────────────────
CSS = b"""
window {
background-color: rgba(52, 28, 74, 0.93);
border: 3px solid rgba(255, 121, 198, 0.78);
border-radius: 14px;
}
#container {
padding: 14px 20px 16px 20px;
}
#vol-icon {
color: #ff79c6;
font-family: "JetBrainsMono Nerd Font";
font-size: 18px;
min-width: 24px;
}
#vol-title {
color: rgba(248, 248, 242, 0.55);
font-family: "JetBrainsMono Nerd Font";
font-size: 11px;
}
#sink-name {
color: #8be9fd;
font-family: "JetBrainsMono Nerd Font";
font-size: 11px;
font-weight: bold;
}
#vol-pct {
color: #f8f8f2;
font-family: "JetBrainsMono Nerd Font";
font-size: 13px;
font-weight: bold;
min-width: 44px;
}
#separator {
color: rgba(92, 73, 108, 0.60);
margin: 4px 0;
}
scale trough {
background-color: rgba(92, 73, 108, 0.55);
border-radius: 3px;
min-height: 6px;
border: none;
}
scale highlight {
background-color: #ff79c6;
border-radius: 3px;
border: none;
}
scale slider {
background-color: #f8f8f2;
border-radius: 50%;
min-width: 18px;
min-height: 18px;
border: 2px solid rgba(255, 121, 198, 0.80);
box-shadow: none;
transition: none;
}
scale slider:hover {
background-color: #e79cfe;
border-color: #ff79c6;
}
#mute-btn {
background: rgba(73, 49, 97, 0.50);
border: 1px solid rgba(92, 73, 108, 0.60);
border-radius: 8px;
color: rgba(248, 248, 242, 0.65);
font-family: "JetBrainsMono Nerd Font";
font-size: 12px;
padding: 5px 16px;
margin-top: 6px;
}
#mute-btn:hover {
background: rgba(255, 121, 198, 0.18);
border-color: rgba(255, 121, 198, 0.45);
color: #ff79c6;
}
#mute-btn.muted {
color: #f38ba8;
border-color: rgba(243, 139, 168, 0.45);
background: rgba(243, 139, 168, 0.10);
}
#mic-btn {
background: rgba(73, 49, 97, 0.50);
border: 1px solid rgba(139, 233, 253, 0.35);
border-radius: 8px;
color: #8be9fd;
font-family: "JetBrainsMono Nerd Font";
font-size: 12px;
padding: 5px 16px;
margin-top: 4px;
}
#mic-btn:hover {
background: rgba(139, 233, 253, 0.12);
border-color: rgba(139, 233, 253, 0.60);
color: #8be9fd;
}
#mic-btn.muted {
color: #f38ba8;
border-color: rgba(243, 139, 168, 0.45);
background: rgba(243, 139, 168, 0.10);
}
"""
POPUP_WIDTH = 300
# ── Audio helpers ─────────────────────────────────────────────────────────────
def get_volume():
"""Retourne (volume 0-100, is_muted)"""
try:
r = subprocess.run(
['wpctl', 'get-volume', '@DEFAULT_AUDIO_SINK@'],
capture_output=True, text=True, timeout=2
)
parts = r.stdout.strip().split()
vol = int(float(parts[1]) * 100)
muted = '[MUTED]' in r.stdout
return min(max(vol, 0), 100), muted
except Exception:
return 50, False
def get_sink_name():
"""Retourne le nom humain de la sortie audio active."""
try:
r = subprocess.run(
['wpctl', 'inspect', '@DEFAULT_AUDIO_SINK@'],
capture_output=True, text=True, timeout=2
)
# Chercher node.description en priorité, sinon node.nick
for field in ('node.description', 'node.nick'):
m = re.search(rf'{field}\s*=\s*"([^"]+)"', r.stdout)
if m:
return m.group(1)
except Exception:
pass
return 'Sortie audio'
def set_volume(vol):
subprocess.run(
['wpctl', 'set-volume', '-l', '1.0', '@DEFAULT_AUDIO_SINK@', f'{vol}%'],
capture_output=True
)
# Feedback wob (non-bloquant)
fifo = '/tmp/wob.fifo'
if os.path.exists(fifo):
try:
fd = os.open(fifo, os.O_WRONLY | os.O_NONBLOCK)
os.write(fd, f'{vol}\n'.encode())
os.close(fd)
except OSError:
pass
def toggle_mute():
subprocess.run(
['wpctl', 'set-mute', '@DEFAULT_AUDIO_SINK@', 'toggle'],
capture_output=True
)
def get_mic_muted():
"""Retourne True si le micro actif est muté."""
try:
r = subprocess.run(
['wpctl', 'get-volume', '@DEFAULT_AUDIO_SOURCE@'],
capture_output=True, text=True, timeout=2
)
return '[MUTED]' in r.stdout
except Exception:
return False
def toggle_mic_mute():
subprocess.run(
['wpctl', 'set-mute', '@DEFAULT_AUDIO_SOURCE@', 'toggle'],
capture_output=True
)
def vol_icon(vol, muted):
if muted or vol == 0:
return '󰝟'
if vol < 50:
return '󰕿'
return '󰕾'
# ── Popup ─────────────────────────────────────────────────────────────────────
class VolumePopup(Gtk.Window):
def __init__(self):
super().__init__()
self._blocked = False
# ── Position : centré sous le module wireplumber ──────────────────────
# Wireplumber = 1er module de la pill droite (côté droit de l'écran).
# On centre le popup horizontalement sous ce module.
display = Gdk.Display.get_default()
monitor = display.get_primary_monitor() if display else None
if monitor:
screen_w = monitor.get_geometry().width
else:
screen_w = 1920 # fallback
# La pill droite a ~16px de marge depuis le bord droit.
# Le module wireplumber est le 1er élément : ~180px depuis le bord droit.
module_center = screen_w - 16 - 180
margin_left = max(0, module_center - POPUP_WIDTH // 2)
GtkLayerShell.init_for_window(self)
GtkLayerShell.set_layer(self, GtkLayerShell.Layer.OVERLAY)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.TOP, True)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.LEFT, True)
GtkLayerShell.set_margin(self, GtkLayerShell.Edge.TOP, 66)
GtkLayerShell.set_margin(self, GtkLayerShell.Edge.LEFT, margin_left)
GtkLayerShell.set_keyboard_mode(self, GtkLayerShell.KeyboardMode.ON_DEMAND)
GtkLayerShell.set_exclusive_zone(self, -1)
self.set_decorated(False)
self.set_resizable(False)
self.set_default_size(POPUP_WIDTH, -1)
# ── CSS ───────────────────────────────────────────────────────────────
provider = Gtk.CssProvider()
provider.load_from_data(CSS)
Gtk.StyleContext.add_provider_for_screen(
Gdk.Screen.get_default(),
provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)
# ── État initial ──────────────────────────────────────────────────────
vol, muted = get_volume()
self._muted = muted
self._mic_muted = get_mic_muted()
sink = get_sink_name()
# ── Layout ────────────────────────────────────────────────────────────
container = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
container.set_name('container')
self.add(container)
# Ligne sink (sortie active)
sink_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
sink_icon = Gtk.Label(label='󰓃')
sink_icon.set_name('vol-title')
sink_row.pack_start(sink_icon, False, False, 0)
self.sink_label = Gtk.Label(label=sink)
self.sink_label.set_name('sink-name')
self.sink_label.set_halign(Gtk.Align.START)
self.sink_label.set_ellipsize(3) # PANGO_ELLIPSIZE_END
sink_row.pack_start(self.sink_label, True, True, 0)
container.pack_start(sink_row, False, False, 0)
# Séparateur
sep = Gtk.Label(label='' * 30)
sep.set_name('separator')
container.pack_start(sep, False, False, 4)
# En-tête volume : icône + "Volume" + %
header = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
self.icon = Gtk.Label(label=vol_icon(vol, muted))
self.icon.set_name('vol-icon')
header.pack_start(self.icon, False, False, 0)
title = Gtk.Label(label='Volume')
title.set_name('vol-title')
title.set_halign(Gtk.Align.START)
header.pack_start(title, True, True, 0)
self.pct = Gtk.Label(label=f'{vol}%')
self.pct.set_name('vol-pct')
self.pct.set_halign(Gtk.Align.END)
header.pack_end(self.pct, False, False, 0)
container.pack_start(header, False, False, 0)
# Slider
self.scale = Gtk.Scale.new_with_range(
Gtk.Orientation.HORIZONTAL, 0, 100, 5
)
self.scale.set_value(vol)
self.scale.set_draw_value(False)
self.scale.set_hexpand(True)
self.scale.connect('value-changed', self._on_changed)
container.pack_start(self.scale, False, False, 0)
# Bouton mute
self.mute_btn = Gtk.Button(label=f'󰖁 {"Remettre le son" if muted else "Muet"}')
self.mute_btn.set_name('mute-btn')
self.mute_btn.set_halign(Gtk.Align.CENTER)
if muted:
self.mute_btn.get_style_context().add_class('muted')
self.mute_btn.connect('clicked', self._on_mute)
container.pack_start(self.mute_btn, False, False, 0)
# ── Section micro ─────────────────────────────────────────────────────
sep2 = Gtk.Label(label='' * 30)
sep2.set_name('separator')
container.pack_start(sep2, False, False, 4)
mic_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
mic_icon = Gtk.Label(label='󰍬')
mic_icon.set_name('vol-title')
mic_icon.set_markup('<span font_family="JetBrainsMono Nerd Font" size="large">󰍬</span>')
mic_row.pack_start(mic_icon, False, False, 0)
mic_title = Gtk.Label(label='Micro')
mic_title.set_name('vol-title')
mic_title.set_halign(Gtk.Align.START)
mic_row.pack_start(mic_title, True, True, 0)
mic_label = '󰍭 Coupé' if self._mic_muted else '󰍬 Actif'
self.mic_btn = Gtk.Button(label=mic_label)
self.mic_btn.set_name('mic-btn')
if self._mic_muted:
self.mic_btn.get_style_context().add_class('muted')
self.mic_btn.connect('clicked', self._on_mic_mute)
mic_row.pack_end(self.mic_btn, False, False, 0)
container.pack_start(mic_row, False, False, 0)
# ── Refresh périodique (détecte changement de sink/micro) ─────────────
GLib.timeout_add(2000, self._refresh_sink)
# ── Fermeture ─────────────────────────────────────────────────────────
self.connect('key-press-event', self._on_key)
self.connect('focus-out-event', lambda *_: self.destroy())
self.show_all()
self.grab_focus()
def _on_changed(self, scale):
if self._blocked:
return
vol = int(scale.get_value())
self.pct.set_label(f'{vol}%')
self.icon.set_label(vol_icon(vol, self._muted))
set_volume(vol)
def _on_mute(self, btn):
toggle_mute()
_, self._muted = get_volume()
vol = int(self.scale.get_value())
self.icon.set_label(vol_icon(vol, self._muted))
if self._muted:
btn.get_style_context().add_class('muted')
btn.set_label('󰕿 Remettre le son')
else:
btn.get_style_context().remove_class('muted')
btn.set_label('󰖁 Muet')
def _refresh_sink(self):
"""Met à jour la sortie et l'état du micro si changement détecté."""
# Sortie audio
sink = get_sink_name()
if self.sink_label.get_label() != sink:
self.sink_label.set_label(sink)
vol, muted = get_volume()
self._blocked = True
self.scale.set_value(vol)
self._blocked = False
self.pct.set_label(f'{vol}%')
self._muted = muted
self.icon.set_label(vol_icon(vol, muted))
# Micro
mic_muted = get_mic_muted()
if mic_muted != self._mic_muted:
self._mic_muted = mic_muted
if mic_muted:
self.mic_btn.get_style_context().add_class('muted')
self.mic_btn.set_label('󰍭 Coupé')
else:
self.mic_btn.get_style_context().remove_class('muted')
self.mic_btn.set_label('󰍬 Actif')
return True # continuer le timer
def _on_mic_mute(self, btn):
toggle_mic_mute()
self._mic_muted = get_mic_muted()
if self._mic_muted:
btn.get_style_context().add_class('muted')
btn.set_label('󰍭 Coupé')
else:
btn.get_style_context().remove_class('muted')
btn.set_label('󰍬 Actif')
def _on_key(self, _widget, event):
if event.keyval == Gdk.KEY_Escape:
self.destroy()
if __name__ == '__main__':
win = VolumePopup()
win.connect('destroy', Gtk.main_quit)
Gtk.main()

View File

@@ -0,0 +1,17 @@
#!/usr/bin/env bash
# wob-brightness.sh — scroll luminosité + feedback wob
# Usage: wob-brightness.sh up|down
STEP=5
FIFO="/tmp/wob.fifo"
case "$1" in
up) brightnessctl set "${STEP}%+" -q ;;
down) brightnessctl set "${STEP}%-" -q ;;
esac
# Feedback overlay
if [[ -p "$FIFO" ]]; then
BRIGHT=$(brightnessctl -m 2>/dev/null | awk -F, '{gsub(/%/,"",$4); print int($4)}')
echo "b:${BRIGHT}" > "$FIFO"
fi

View File

@@ -0,0 +1,227 @@
#!/usr/bin/env python3
# wob-overlay.py — Overlay volume/luminosité violet-chaton (remplace wob)
# Protocole FIFO : "v:N" = volume (rose), "b:N" = luminosité (cyan), "N" = volume
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GtkLayerShell', '0.1')
from gi.repository import Gtk, Gdk, GtkLayerShell, GLib
import os
import threading
FIFO = '/tmp/wob.fifo'
POPUP_WIDTH = 280
POPUP_HEIGHT = -1
MARGIN_BOTTOM = 48
HIDE_DELAY = 1200 # ms
CSS = """
window {
background: transparent;
}
#wob-box {
background-color: rgba(38, 21, 55, 0.92);
border: 2px solid rgba(92, 73, 108, 0.80);
border-radius: 12px;
padding: 10px 16px;
}
#wob-box.vol {
border-color: rgba(255, 121, 198, 0.85);
}
#wob-box.bright {
border-color: rgba(139, 233, 253, 0.85);
}
#wob-icon {
font-family: "JetBrainsMono Nerd Font";
font-size: 15px;
min-width: 22px;
margin-right: 4px;
}
#wob-pct {
color: #f8f8f2;
font-family: "JetBrainsMono Nerd Font";
font-size: 11px;
font-weight: bold;
min-width: 36px;
margin-left: 6px;
}
/* ── Volume (rose) ──────────────────────────────────────────── */
progressbar.vol trough {
background-color: rgba(92, 73, 108, 0.45);
border-radius: 5px;
min-height: 7px;
border: none;
}
progressbar.vol progress {
background-color: #ff79c6;
border-radius: 5px;
border: none;
min-height: 7px;
}
/* ── Luminosité (cyan) ──────────────────────────────────────── */
progressbar.bright trough {
background-color: rgba(92, 73, 108, 0.45);
border-radius: 5px;
min-height: 7px;
border: none;
}
progressbar.bright progress {
background-color: #8be9fd;
border-radius: 5px;
border: none;
min-height: 7px;
}
"""
VOL_ICONS = ['󰕿', '󰖀', '󰕾']
BRIGHT_ICONS = ['󰃞', '󰃟', '󰃠']
def _icon(icons, pct):
if pct <= 0:
return icons[0]
if pct < 50:
return icons[1]
return icons[2]
class WobOverlay(Gtk.Window):
def __init__(self):
super().__init__()
self._hide_timer = None
# ── Layer shell ──────────────────────────────────────────────────────
GtkLayerShell.init_for_window(self)
GtkLayerShell.set_layer(self, GtkLayerShell.Layer.OVERLAY)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.BOTTOM, True)
GtkLayerShell.set_margin(self, GtkLayerShell.Edge.BOTTOM, MARGIN_BOTTOM)
GtkLayerShell.set_exclusive_zone(self, -1)
GtkLayerShell.set_keyboard_mode(self, GtkLayerShell.KeyboardMode.NONE)
self.set_decorated(False)
self.set_resizable(False)
self.set_app_paintable(True)
self.set_default_size(POPUP_WIDTH, POPUP_HEIGHT)
# Fenêtre transparente
screen = self.get_screen()
visual = screen.get_rgba_visual()
if visual:
self.set_visual(visual)
# ── CSS ──────────────────────────────────────────────────────────────
provider = Gtk.CssProvider()
provider.load_from_data(CSS.encode())
Gtk.StyleContext.add_provider_for_screen(
Gdk.Screen.get_default(), provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)
# ── Layout ───────────────────────────────────────────────────────────
outer = Gtk.Box()
outer.set_name('wob-box')
self._box = outer
self.add(outer)
self._icon_lbl = Gtk.Label()
self._icon_lbl.set_name('wob-icon')
outer.pack_start(self._icon_lbl, False, False, 0)
self._bar = Gtk.ProgressBar()
self._bar.set_hexpand(True)
self._bar.set_valign(Gtk.Align.CENTER)
outer.pack_start(self._bar, True, True, 0)
self._pct_lbl = Gtk.Label()
self._pct_lbl.set_name('wob-pct')
self._pct_lbl.set_halign(Gtk.Align.END)
outer.pack_end(self._pct_lbl, False, False, 0)
self.show_all()
self.hide()
def show_value(self, val, kind='v'):
"""Appelé depuis le thread FIFO via GLib.idle_add."""
val = max(0, min(100, val))
fraction = val / 100.0
# Icône + couleur selon le type
if kind == 'b':
icon = _icon(BRIGHT_ICONS, val)
css_class = 'bright'
else:
icon = _icon(VOL_ICONS, val)
css_class = 'vol'
sc = self._bar.get_style_context()
bsc = self._box.get_style_context()
for c in ['vol', 'bright']:
sc.remove_class(c)
bsc.remove_class(c)
sc.add_class(css_class)
bsc.add_class(css_class)
self._icon_lbl.set_label(icon)
self._icon_lbl.set_markup(
f'<span foreground="{"#8be9fd" if kind == "b" else "#ff79c6"}">{icon}</span>'
)
self._bar.set_fraction(fraction)
self._pct_lbl.set_label(f'{val}%')
self.show()
# Reset timer
if self._hide_timer:
GLib.source_remove(self._hide_timer)
self._hide_timer = GLib.timeout_add(HIDE_DELAY, self._do_hide)
def _do_hide(self):
self.hide()
self._hide_timer = None
return False
def _fifo_reader(overlay):
"""Thread : lit le FIFO en boucle et schedule les mises à jour UI."""
while True:
try:
if not os.path.exists(FIFO):
os.mkfifo(FIFO)
with open(FIFO, 'r') as f:
for line in f:
line = line.strip()
if not line:
continue
# Protocole : "v:N", "b:N" ou plain "N"
if ':' in line:
kind, _, raw = line.partition(':')
else:
kind, raw = 'v', line
try:
val = int(raw)
GLib.idle_add(overlay.show_value, val, kind)
except ValueError:
pass
except OSError:
import time
time.sleep(0.5)
if __name__ == '__main__':
win = WobOverlay()
win.connect('destroy', Gtk.main_quit)
t = threading.Thread(target=_fifo_reader, args=(win,), daemon=True)
t.start()
Gtk.main()

View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
# wob-start.sh — lance l'overlay violet-chaton via FIFO
# Appelé au démarrage de session (autostart)
FIFO="/tmp/wob.fifo"
pkill -f wob-overlay.py 2>/dev/null
rm -f "$FIFO"
mkfifo "$FIFO"
# Ouvrir le FIFO en lecture+écriture sur fd3 :
# empêche l'overlay de recevoir EOF entre deux écritures
exec 3<> "$FIFO"
python3 "$HOME/.config/waybar/scripts/wob-overlay.py" &
disown $!

View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# wob-volume.sh — scroll volume + feedback wob
# Usage: wob-volume.sh up|down|mute
STEP=5
FIFO="/tmp/wob.fifo"
get_vol() {
wpctl get-volume @DEFAULT_AUDIO_SINK@ 2>/dev/null | \
LC_ALL=C awk '{v=int($2*100); if(v>100)v=100; print v}'
}
case "$1" in
up)
wpctl set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ "${STEP}%+"
;;
down)
wpctl set-volume @DEFAULT_AUDIO_SINK@ "${STEP}%-"
;;
mute)
wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
;;
esac
# Feedback overlay
if [[ -p "$FIFO" ]]; then
echo "v:$(get_vol)" > "$FIFO"
fi

View File

@@ -0,0 +1,20 @@
# ── violet-chaton wob config (v0.14.2) ───────────────────────────────────────
timeout = 1000
max = 100
width = 260
height = 36
border_offset = 4
border_size = 2
bar_padding = 4
anchor = bottom
margin = 40
background_color = 261537e0
border_color = 5C496Ccc
bar_color = ff79c6ff
overflow_mode = wrap
overflow_bar_color = f38ba8ff
overflow_background_color = 261537e0
overflow_border_color = f38ba8cc

View File

@@ -0,0 +1,7 @@
allow_markup=true
insensitive=true
hide_scroll=true
matching=contains
no_actions=true
layer=overlay
lines=15

View File

@@ -12,6 +12,14 @@ export INSTALL_LOG="$HOME/violet-chaton-install-$(date +%Y%m%d-%H%M%S).log"
source "$SCRIPT_DIR/scripts/lib.sh"
# ── Refus root ────────────────────────────────────────────────────────────────
if [ "$EUID" -eq 0 ]; then
echo -e "${RED}${BOLD} ERREUR : Ne pas lancer ce script en tant que root !${RESET}"
echo -e " Lance-le en tant qu'utilisateur normal : ${CYAN}bash install.sh${RESET}"
echo -e " ${MUTED}(sudo sera demandé automatiquement quand nécessaire)${RESET}"
exit 1
fi
# ── Vérifications préalables ──────────────────────────────────────────────────
check_requirements() {
local ok=true

View File

@@ -19,6 +19,7 @@ PACKAGES=(
chafa
jq
libgtk-3-bin
adw-gtk3
nemo
nemo-fileroller
# fastfetch → installé via .deb GitHub (voir 02-packages-manual.sh)
@@ -26,6 +27,16 @@ PACKAGES=(
btop
# atuin → installé via son propre script (voir 02-packages-manual.sh)
# starship → installé via son propre script (voir 02-packages-manual.sh)
# ── Waybar + launcher + dépendances ──────────────────────────────────────
waybar
wob
wofi
brightnessctl
playerctl
wireplumber
python3-gi
gir1.2-gtk-3.0
gir1.2-gtklayershell-0.1
# ── Fun & utils ──────────────────────────────────────────────────────────
cmatrix
toilet

View File

@@ -11,7 +11,14 @@ deploy_file() {
local src="$1"
local dst="$2"
ensure_dir "$(dirname "$dst")"
if [ -f "$dst" ]; then
if [ -L "$dst" ]; then
# Symlink géré par COSMIC : sauvegarder la cible réelle puis supprimer le lien
local real; real=$(readlink -f "$dst")
local rel="${dst#"$HOME/"}"
ensure_dir "$BACKUP_DIR/$(dirname "$rel")"
cp "$real" "$BACKUP_DIR/$rel" 2>/dev/null
rm "$dst"
elif [ -f "$dst" ]; then
local rel="${dst#"$HOME/"}"
ensure_dir "$BACKUP_DIR/$(dirname "$rel")"
cp "$dst" "$BACKUP_DIR/$rel" 2>/dev/null
@@ -82,6 +89,24 @@ else
fail "CosmicTheme.Dark"
fi
step "COSMIC Theme Light..."
backup_dir "$COSMIC_DST/com.system76.CosmicTheme.Light/v1"
ensure_dir "$COSMIC_DST/com.system76.CosmicTheme.Light/v1"
if cp "$COSMIC_SRC/com.system76.CosmicTheme.Light/v1/"* "$COSMIC_DST/com.system76.CosmicTheme.Light/v1/" 2>/dev/null; then
ok "CosmicTheme.Light"
else
fail "CosmicTheme.Light"
fi
step "COSMIC AppList (favoris dock — pas d'apps en cours)..."
backup_dir "$COSMIC_DST/com.system76.CosmicAppList/v1"
ensure_dir "$COSMIC_DST/com.system76.CosmicAppList/v1"
if cp "$COSMIC_SRC/com.system76.CosmicAppList/v1/"* "$COSMIC_DST/com.system76.CosmicAppList/v1/" 2>/dev/null; then
ok "CosmicAppList (filter_top_levels)"
else
fail "CosmicAppList"
fi
step "COSMIC Theme Mode (dark)..."
backup_dir "$COSMIC_DST/com.system76.CosmicTheme.Mode/v1"
ensure_dir "$COSMIC_DST/com.system76.CosmicTheme.Mode/v1"
@@ -100,11 +125,27 @@ else
fail "CosmicTerm"
fi
# ── GTK3 — thème violet-chaton ─────────────────────────────────────────────
section "GTK3 — thème violet-chaton"
# ── GTK3 / GTK4 — thème violet-chaton ─────────────────────────────────────
section "GTK — thème violet-chaton"
step "Thème GTK3 (adw-gtk3-dark + couleurs violet-chaton)..."
ensure_dir "$HOME/.config/gtk-3.0"
deploy_file "$THEMES/violet-chaton-gtk.css" "$HOME/.config/gtk-3.0/gtk.css"
step "Thème GTK4 / libadwaita (couleurs violet-chaton)..."
ensure_dir "$HOME/.config/gtk-4.0"
deploy_file "$THEMES/violet-chaton-gtk.css" "$HOME/.config/gtk-4.0/gtk.css"
step "Activation adw-gtk3-dark + dark mode (gsettings)..."
if has_cmd gsettings; then
gsettings set org.gnome.desktop.interface gtk-theme 'adw-gtk3-dark' 2>/dev/null && \
gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' 2>/dev/null && \
ok "gtk-theme=adw-gtk3-dark, color-scheme=prefer-dark" || \
warn "gsettings GTK échoué — thème à appliquer manuellement"
else
warn "gsettings non disponible — thème GTK à appliquer manuellement"
fi
# ── Nemo — gestionnaire de fichiers ────────────────────────────────────────
section "Nemo — configuration et thème"
@@ -205,6 +246,105 @@ PYEOF
fi
fi
# ── Waybar ─────────────────────────────────────────────────────────────────────
section "Waybar — island floating 3 pills"
deploy_file "$CONFIGS/waybar/config" "$HOME/.config/waybar/config"
deploy_file "$THEMES/violet-chaton-waybar.css" "$HOME/.config/waybar/style.css"
deploy_file "$CONFIGS/waybar/cava-waybar.cfg" "$HOME/.config/waybar/cava-waybar.cfg"
step "Scripts waybar..."
ensure_dir "$HOME/.config/waybar/scripts"
for script in "$CONFIGS/waybar/scripts/"*.sh; do
dst="$HOME/.config/waybar/scripts/$(basename "$script")"
cp "$script" "$dst" && chmod +x "$dst"
done
for script in "$CONFIGS/waybar/scripts/"*.py; do
dst="$HOME/.config/waybar/scripts/$(basename "$script")"
cp "$script" "$dst" && chmod +x "$dst"
done
ok "Scripts waybar"
# ── Autostart ───────────────────────────────────────────────────────────────────
section "Autostart — waybar + wob"
ensure_dir "$HOME/.config/autostart"
deploy_file "$CONFIGS/autostart/waybar.desktop" "$HOME/.config/autostart/waybar.desktop"
deploy_file "$CONFIGS/autostart/wob.desktop" "$HOME/.config/autostart/wob.desktop"
# ── Règle sudoers — profil énergie ──────────────────────────────────────────────
section "Sudoers — profil énergie ACPI"
SUDOERS_FILE="/etc/sudoers.d/waybar-power-profile"
SUDOERS_RULE="$USER ALL=(ALL) NOPASSWD: /usr/bin/tee /sys/firmware/acpi/platform_profile"
if [ -f "$SUDOERS_FILE" ]; then
ok "Règle sudoers déjà présente"
else
step "Création de la règle sudoers (mot de passe sudo requis)..."
echo "$SUDOERS_RULE" > /tmp/waybar-pp-sudoers
if sudo cp /tmp/waybar-pp-sudoers "$SUDOERS_FILE" && sudo chmod 440 "$SUDOERS_FILE"; then
rm -f /tmp/waybar-pp-sudoers
ok "Règle sudoers créée"
else
rm -f /tmp/waybar-pp-sudoers
warn "Échec sudoers — changement de profil énergie nécessitera sudo"
fi
fi
# ── Règle udev — permissions platform_profile ────────────────────────────────
section "udev — platform_profile accessible au groupe video"
UDEV_FILE="/etc/udev/rules.d/90-platform-profile.rules"
if [ -f "$UDEV_FILE" ]; then
ok "Règle udev déjà présente"
else
step "Création de la règle udev (mot de passe sudo requis)..."
echo 'ACTION=="add|change", KERNEL=="platform_profile", SUBSYSTEM=="acpi", RUN+="/bin/chmod g+w /sys/firmware/acpi/platform_profile", RUN+="/bin/chgrp video /sys/firmware/acpi/platform_profile"' \
> /tmp/90-platform-profile.rules
if sudo cp /tmp/90-platform-profile.rules "$UDEV_FILE" && sudo chmod 644 "$UDEV_FILE"; then
rm -f /tmp/90-platform-profile.rules
sudo chmod g+w /sys/firmware/acpi/platform_profile 2>/dev/null
sudo chgrp video /sys/firmware/acpi/platform_profile 2>/dev/null
ok "Règle udev créée"
else
rm -f /tmp/90-platform-profile.rules
warn "Échec udev — redémarrage requis pour les permissions platform_profile"
fi
fi
# ── Wofi — launcher + power menu ────────────────────────────────────────────────
section "Wofi — launcher violet-chaton"
ensure_dir "$HOME/.config/wofi"
deploy_file "$CONFIGS/wofi/config" "$HOME/.config/wofi/config"
deploy_file "$THEMES/violet-chaton-wofi.css" "$HOME/.config/wofi/style.css"
deploy_file "$THEMES/violet-chaton-wofi-power.css" "$HOME/.config/wofi/power-style.css"
# ── Rofi ────────────────────────────────────────────────────────────────────────
section "Rofi — thème violet-chaton"
ensure_dir "$HOME/.config/rofi"
deploy_file "$CONFIGS/rofi/config.rasi" "$HOME/.config/rofi/config.rasi"
deploy_file "$THEMES/violet-chaton-rofi.rasi" "$HOME/.config/rofi/violet-chaton.rasi"
# ── wob ─────────────────────────────────────────────────────────────────────────
section "wob — overlay volume/luminosité"
deploy_file "$CONFIGS/wob/wob.ini" "$HOME/.config/wob.ini"
# ── Désactiver cosmic-osd (remplacé par wob) ─────────────────────────────────
section "cosmic-osd — désactivation (wob le remplace)"
if [ -f /usr/bin/cosmic-osd.real ]; then
ok "cosmic-osd déjà désactivé via dpkg-divert"
elif [ -f /usr/bin/cosmic-osd ]; then
step "Divert de cosmic-osd (mot de passe sudo requis)..."
if sudo dpkg-divert --add --rename --divert /usr/bin/cosmic-osd.real /usr/bin/cosmic-osd; then
printf '#!/usr/bin/env bash\nexec sleep infinity\n' > /tmp/cosmic-osd-fake
chmod +x /tmp/cosmic-osd-fake
sudo cp /tmp/cosmic-osd-fake /usr/bin/cosmic-osd
rm -f /tmp/cosmic-osd-fake
pkill -x cosmic-osd 2>/dev/null
ok "cosmic-osd désactivé — wob gère les overlays"
else
warn "dpkg-divert échoué — cosmic-osd reste actif"
fi
else
warn "cosmic-osd introuvable — rien à faire"
fi
# ── Logo fastfetch ─────────────────────────────────────────────────────────────
section "Logo fastfetch"
if [ -f "$SCRIPT_DIR/assets/violet-chaton-logo.png" ]; then

View File

@@ -0,0 +1 @@
Some([])

View File

@@ -0,0 +1,74 @@
(
base: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
hover: (
red: 0.7988549,
green: 0.5659659,
blue: 0.8900906,
alpha: 1.0,
),
pressed: (
red: 0.81436175,
green: 0.6745583,
blue: 0.91372323,
alpha: 1.0,
),
selected: (
red: 0.7988549,
green: 0.5659659,
blue: 0.8900906,
alpha: 1.0,
),
selected_text: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
focus: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
divider: (
red: 0.9999203,
green: 0.99998146,
blue: 1.0,
alpha: 1.0,
),
on: (
red: 0.9999203,
green: 0.99998146,
blue: 1.0,
alpha: 1.0,
),
disabled: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
on_disabled: (
red: 0.9529013,
green: 0.8058731,
blue: 0.99803925,
alpha: 1.0,
),
border: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
disabled_border: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 0.5,
),
)

View File

@@ -0,0 +1,74 @@
(
base: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
hover: (
red: 0.7988549,
green: 0.5659659,
blue: 0.8900906,
alpha: 1.0,
),
pressed: (
red: 0.81436175,
green: 0.6745583,
blue: 0.91372323,
alpha: 1.0,
),
selected: (
red: 0.7988549,
green: 0.5659659,
blue: 0.8900906,
alpha: 1.0,
),
selected_text: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
focus: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
divider: (
red: 0.84878576,
green: 0.8639131,
blue: 0.9608068,
alpha: 1.0,
),
on: (
red: 0.9999203,
green: 0.99998146,
blue: 1.0,
alpha: 1.0,
),
disabled: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
on_disabled: (
red: 0.9999203,
green: 0.99998146,
blue: 1.0,
alpha: 0.5,
),
border: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
disabled_border: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 0.5,
),
)

View File

@@ -0,0 +1 @@
None

View File

@@ -0,0 +1,100 @@
(
base: (
red: 0.20392156,
green: 0.109803915,
blue: 0.2901961,
alpha: 1.0,
),
component: (
base: (
red: 0.28618598,
green: 0.19189146,
blue: 0.3797182,
alpha: 1.0,
),
hover: (
red: 0.25756738,
green: 0.17270231,
blue: 0.3417464,
alpha: 1.0,
),
pressed: (
red: 0.22894879,
green: 0.15351318,
blue: 0.30377457,
alpha: 1.0,
),
selected: (
red: 0.25756738,
green: 0.17270231,
blue: 0.3417464,
alpha: 1.0,
),
selected_text: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
focus: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
divider: (
red: 0.8318751,
green: 0.8318648,
blue: 0.80908096,
alpha: 0.2,
),
on: (
red: 0.8318751,
green: 0.8318648,
blue: 0.80908096,
alpha: 1.0,
),
disabled: (
red: 0.28618598,
green: 0.19189146,
blue: 0.3797182,
alpha: 0.5,
),
on_disabled: (
red: 0.8318751,
green: 0.8318648,
blue: 0.80908096,
alpha: 0.65,
),
border: (
red: 0.072988935,
green: 0.079405405,
blue: 0.14595589,
alpha: 1.0,
),
disabled_border: (
red: 0.072988935,
green: 0.079405405,
blue: 0.14595589,
alpha: 0.5,
),
),
divider: (
red: 0.3609102,
green: 0.285616,
blue: 0.42522332,
alpha: 1.0,
),
on: (
red: 0.98886484,
green: 0.98886436,
blue: 0.9653322,
alpha: 1.0,
),
small_widget: (
red: 0.20783836,
green: 0.20930338,
blue: 0.23179808,
alpha: 0.25,
),
)

View File

@@ -0,0 +1,74 @@
(
base: (
red: 0.16402894,
green: 0.17314792,
blue: 0.246458,
alpha: 0.25,
),
hover: (
red: 0.15616347,
green: 0.16312802,
blue: 0.2164568,
alpha: 0.4,
),
pressed: (
red: 0.3942264,
green: 0.40330547,
blue: 0.46497566,
alpha: 0.625,
),
selected: (
red: 0.15616347,
green: 0.16312802,
blue: 0.2164568,
alpha: 0.4,
),
selected_text: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
focus: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
divider: (
red: 0.8318751,
green: 0.8318648,
blue: 0.80908096,
alpha: 0.2,
),
on: (
red: 0.8318751,
green: 0.8318648,
blue: 0.80908096,
alpha: 1.0,
),
disabled: (
red: 0.16402894,
green: 0.17314792,
blue: 0.246458,
alpha: 0.125,
),
on_disabled: (
red: 0.8318751,
green: 0.8318648,
blue: 0.80908096,
alpha: 0.65,
),
border: (
red: 0.072988935,
green: 0.079405405,
blue: 0.14595589,
alpha: 1.0,
),
disabled_border: (
red: 0.072988935,
green: 0.079405405,
blue: 0.14595589,
alpha: 0.5,
),
)

View File

@@ -0,0 +1,5 @@
Some((
red: 0.46666667,
green: 0.46666667,
blue: 0.46666667,
))

View File

@@ -0,0 +1,8 @@
(
radius_0: (0.0, 0.0, 0.0, 0.0),
radius_xs: (2.0, 2.0, 2.0, 2.0),
radius_s: (8.0, 8.0, 8.0, 8.0),
radius_m: (8.0, 8.0, 8.0, 8.0),
radius_l: (8.0, 8.0, 8.0, 8.0),
radius_xl: (8.0, 8.0, 8.0, 8.0),
)

View File

@@ -0,0 +1,74 @@
(
base: (
red: 0.47058824,
green: 0.16078432,
blue: 0.18039216,
alpha: 1.0,
),
hover: (
red: 0.45061958,
green: 0.20518154,
blue: 0.23754153,
alpha: 1.0,
),
pressed: (
red: 0.59671474,
green: 0.44906804,
blue: 0.5058801,
alpha: 1.0,
),
selected: (
red: 0.45061958,
green: 0.20518154,
blue: 0.23754153,
alpha: 1.0,
),
selected_text: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
focus: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
divider: (
red: 0.9999203,
green: 0.99998146,
blue: 1.0,
alpha: 1.0,
),
on: (
red: 0.9999203,
green: 0.99998146,
blue: 1.0,
alpha: 1.0,
),
disabled: (
red: 0.47058824,
green: 0.16078432,
blue: 0.18039216,
alpha: 1.0,
),
on_disabled: (
red: 0.7352543,
green: 0.5803829,
blue: 0.5901961,
alpha: 1.0,
),
border: (
red: 0.47058824,
green: 0.16078432,
blue: 0.18039216,
alpha: 1.0,
),
disabled_border: (
red: 0.47058824,
green: 0.16078432,
blue: 0.18039216,
alpha: 0.5,
),
)

View File

@@ -0,0 +1,74 @@
(
base: (
red: 0.47058824,
green: 0.16078432,
blue: 0.18039216,
alpha: 1.0,
),
hover: (
red: 0.45061958,
green: 0.20518154,
blue: 0.23754153,
alpha: 1.0,
),
pressed: (
red: 0.59671474,
green: 0.44906804,
blue: 0.5058801,
alpha: 1.0,
),
selected: (
red: 0.45061958,
green: 0.20518154,
blue: 0.23754153,
alpha: 1.0,
),
selected_text: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
focus: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
divider: (
red: 0.84878576,
green: 0.8639131,
blue: 0.9608068,
alpha: 1.0,
),
on: (
red: 0.9999203,
green: 0.99998146,
blue: 1.0,
alpha: 1.0,
),
disabled: (
red: 0.47058824,
green: 0.16078432,
blue: 0.18039216,
alpha: 1.0,
),
on_disabled: (
red: 0.9999203,
green: 0.99998146,
blue: 1.0,
alpha: 0.5,
),
border: (
red: 0.47058824,
green: 0.16078432,
blue: 0.18039216,
alpha: 1.0,
),
disabled_border: (
red: 0.47058824,
green: 0.16078432,
blue: 0.18039216,
alpha: 0.5,
),
)

View File

@@ -0,0 +1 @@
(0, 8)

View File

@@ -0,0 +1,74 @@
(
base: (
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 0.0,
),
hover: (
red: 0.37074497,
green: 0.38277033,
blue: 0.46613896,
alpha: 0.2,
),
pressed: (
red: 0.7228412,
green: 0.7373518,
blue: 0.8313681,
alpha: 0.5,
),
selected: (
red: 0.37074497,
green: 0.38277033,
blue: 0.46613896,
alpha: 0.2,
),
selected_text: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
focus: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
divider: (
red: 0.072988935,
green: 0.079405405,
blue: 0.14595589,
alpha: 0.2,
),
on: (
red: 0.072988935,
green: 0.079405405,
blue: 0.14595589,
alpha: 1.0,
),
disabled: (
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 0.0,
),
on_disabled: (
red: 0.072988935,
green: 0.079405405,
blue: 0.14595589,
alpha: 0.65,
),
border: (
red: 0.072988935,
green: 0.079405405,
blue: 0.14595589,
alpha: 1.0,
),
disabled_border: (
red: 0.072988935,
green: 0.079405405,
blue: 0.14595589,
alpha: 0.5,
),
)

View File

@@ -0,0 +1 @@
false

View File

@@ -0,0 +1 @@
false

View File

@@ -0,0 +1,74 @@
(
base: (
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 0.0,
),
hover: (
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 0.0,
),
pressed: (
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 0.0,
),
selected: (
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 0.0,
),
selected_text: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
focus: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
divider: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 0.2,
),
on: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
disabled: (
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 0.0,
),
on_disabled: (
red: 0.45294115,
green: 0.30588236,
blue: 0.49803922,
alpha: 0.5,
),
border: (
red: 0.072988935,
green: 0.079405405,
blue: 0.14595589,
alpha: 1.0,
),
disabled_border: (
red: 0.072988935,
green: 0.079405405,
blue: 0.14595589,
alpha: 0.5,
),
)

View File

@@ -0,0 +1 @@
"Violet-chaton"

View File

@@ -0,0 +1,195 @@
(
name: "cosmic-light",
bright_red: (
red: 0.5372549,
green: 0.015686275,
blue: 0.09411765,
alpha: 1.0,
),
bright_green: (
red: 0.0,
green: 0.34117648,
blue: 0.17254902,
alpha: 1.0,
),
bright_orange: (
red: 0.4745098,
green: 0.17254902,
blue: 0.0,
alpha: 1.0,
),
gray_1: (
red: 0.8666667,
green: 0.8666667,
blue: 0.8666667,
alpha: 1.0,
),
gray_2: (
red: 0.9098039,
green: 0.9098039,
blue: 0.9098039,
alpha: 1.0,
),
neutral_0: (
red: 1.0,
green: 1.0,
blue: 1.0,
alpha: 1.0,
),
neutral_1: (
red: 0.8862745,
green: 0.8862745,
blue: 0.8862745,
alpha: 1.0,
),
neutral_2: (
red: 0.7764706,
green: 0.7764706,
blue: 0.7764706,
alpha: 1.0,
),
neutral_3: (
red: 0.67058825,
green: 0.67058825,
blue: 0.67058825,
alpha: 1.0,
),
neutral_4: (
red: 0.5686275,
green: 0.5686275,
blue: 0.5686275,
alpha: 1.0,
),
neutral_5: (
red: 0.46666667,
green: 0.46666667,
blue: 0.46666667,
alpha: 1.0,
),
neutral_6: (
red: 0.36862746,
green: 0.36862746,
blue: 0.36862746,
alpha: 1.0,
),
neutral_7: (
red: 0.2784314,
green: 0.2784314,
blue: 0.2784314,
alpha: 1.0,
),
neutral_8: (
red: 0.1882353,
green: 0.1882353,
blue: 0.1882353,
alpha: 1.0,
),
neutral_9: (
red: 0.105882354,
green: 0.105882354,
blue: 0.105882354,
alpha: 1.0,
),
neutral_10: (
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 1.0,
),
accent_blue: (
red: 0.0,
green: 0.32156864,
blue: 0.3529412,
alpha: 1.0,
),
accent_indigo: (
red: 0.18039216,
green: 0.28627452,
blue: 0.42745098,
alpha: 1.0,
),
accent_purple: (
red: 0.40784314,
green: 0.12941177,
blue: 0.4862745,
alpha: 1.0,
),
accent_pink: (
red: 0.5254902,
green: 0.015686275,
blue: 0.22745098,
alpha: 1.0,
),
accent_red: (
red: 0.47058824,
green: 0.16078432,
blue: 0.18039216,
alpha: 1.0,
),
accent_orange: (
red: 0.38431373,
green: 0.2509804,
blue: 0.0,
alpha: 1.0,
),
accent_yellow: (
red: 0.3254902,
green: 0.28235295,
blue: 0.0,
alpha: 1.0,
),
accent_green: (
red: 0.09411765,
green: 0.33333334,
blue: 0.16078432,
alpha: 1.0,
),
accent_warm_grey: (
red: 0.33333334,
green: 0.2784314,
blue: 0.25882354,
alpha: 1.0,
),
ext_warm_grey: (
red: 0.60784316,
green: 0.5568628,
blue: 0.5411765,
alpha: 1.0,
),
ext_orange: (
red: 0.9843137,
green: 0.72156864,
blue: 0.42352942,
alpha: 1.0,
),
ext_yellow: (
red: 0.96862745,
green: 0.8784314,
blue: 0.38431373,
alpha: 1.0,
),
ext_blue: (
red: 0.41568628,
green: 0.7921569,
blue: 0.84705883,
alpha: 1.0,
),
ext_purple: (
red: 0.8352941,
green: 0.54901963,
blue: 1.0,
alpha: 1.0,
),
ext_pink: (
red: 1.0,
green: 0.6117647,
blue: 0.8666667,
alpha: 1.0,
),
ext_indigo: (
red: 0.58431375,
green: 0.76862746,
blue: 0.9882353,
alpha: 1.0,
),
)

View File

@@ -0,0 +1,100 @@
(
base: (
red: 0.21960787,
green: 0.13725492,
blue: 0.29411766,
alpha: 1.0,
),
component: (
base: (
red: 0.28282174,
green: 0.19933021,
blue: 0.36183798,
alpha: 1.0,
),
hover: (
red: 0.25453955,
green: 0.17939718,
blue: 0.32565418,
alpha: 1.0,
),
pressed: (
red: 0.2262574,
green: 0.15946417,
blue: 0.2894704,
alpha: 1.0,
),
selected: (
red: 0.25453955,
green: 0.17939718,
blue: 0.32565418,
alpha: 1.0,
),
selected_text: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
focus: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
divider: (
red: 0.8318751,
green: 0.8318648,
blue: 0.80908096,
alpha: 0.2,
),
on: (
red: 0.8318751,
green: 0.8318648,
blue: 0.80908096,
alpha: 1.0,
),
disabled: (
red: 0.28282174,
green: 0.19933021,
blue: 0.36183798,
alpha: 0.5,
),
on_disabled: (
red: 0.8318751,
green: 0.8318648,
blue: 0.80908096,
alpha: 0.65,
),
border: (
red: 0.072988935,
green: 0.079405405,
blue: 0.14595589,
alpha: 1.0,
),
disabled_border: (
red: 0.072988935,
green: 0.079405405,
blue: 0.14595589,
alpha: 0.5,
),
),
divider: (
red: 0.32675916,
green: 0.26087362,
blue: 0.38188773,
alpha: 1.0,
),
on: (
red: 0.7553642,
green: 0.7553483,
blue: 0.732968,
alpha: 1.0,
),
small_widget: (
red: 0.22808892,
green: 0.22971416,
blue: 0.25254303,
alpha: 0.25,
),
)

View File

@@ -0,0 +1,100 @@
(
base: (
red: 0.30756307,
green: 0.21295837,
blue: 0.4027321,
alpha: 1.0,
),
component: (
base: (
red: 0.3401073,
green: 0.24494115,
blue: 0.4376148,
alpha: 1.0,
),
hover: (
red: 0.30609655,
green: 0.22044702,
blue: 0.3938533,
alpha: 1.0,
),
pressed: (
red: 0.27208585,
green: 0.19595292,
blue: 0.35009184,
alpha: 1.0,
),
selected: (
red: 0.30609655,
green: 0.22044702,
blue: 0.3938533,
alpha: 1.0,
),
selected_text: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
focus: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
divider: (
red: 0.8966642,
green: 0.8966583,
blue: 0.87355375,
alpha: 0.2,
),
on: (
red: 0.8966642,
green: 0.8966583,
blue: 0.87355375,
alpha: 1.0,
),
disabled: (
red: 0.3401073,
green: 0.24494115,
blue: 0.4376148,
alpha: 0.5,
),
on_disabled: (
red: 0.8966642,
green: 0.8966583,
blue: 0.87355375,
alpha: 0.65,
),
border: (
red: 0.072988935,
green: 0.079405405,
blue: 0.14595589,
alpha: 1.0,
),
disabled_border: (
red: 0.072988935,
green: 0.079405405,
blue: 0.14595589,
alpha: 0.5,
),
),
divider: (
red: 0.41758668,
green: 0.34190118,
blue: 0.48913747,
alpha: 1.0,
),
on: (
red: 0.8576811,
green: 0.8576725,
blue: 0.83475876,
alpha: 1.0,
),
small_widget: (
red: 0.31199428,
green: 0.31415156,
blue: 0.33818555,
alpha: 0.25,
),
)

View File

@@ -0,0 +1,6 @@
(
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 0.08,
)

View File

@@ -0,0 +1,12 @@
(
space_none: 0,
space_xxxs: 4,
space_xxs: 4,
space_xs: 8,
space_s: 8,
space_m: 16,
space_l: 24,
space_xl: 32,
space_xxl: 48,
space_xxxl: 64,
)

View File

@@ -0,0 +1,74 @@
(
base: (
red: 0.09411765,
green: 0.33333334,
blue: 0.16078432,
alpha: 1.0,
),
hover: (
red: 0.14944312,
green: 0.34322074,
blue: 0.22185525,
alpha: 1.0,
),
pressed: (
red: 0.40847942,
green: 0.5353426,
blue: 0.4960762,
alpha: 1.0,
),
selected: (
red: 0.14944312,
green: 0.34322074,
blue: 0.22185525,
alpha: 1.0,
),
selected_text: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
focus: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
divider: (
red: 0.9999203,
green: 0.99998146,
blue: 1.0,
alpha: 1.0,
),
on: (
red: 0.9999203,
green: 0.99998146,
blue: 1.0,
alpha: 1.0,
),
disabled: (
red: 0.09411765,
green: 0.33333334,
blue: 0.16078432,
alpha: 1.0,
),
on_disabled: (
red: 0.547019,
green: 0.6666574,
blue: 0.5803922,
alpha: 1.0,
),
border: (
red: 0.09411765,
green: 0.33333334,
blue: 0.16078432,
alpha: 1.0,
),
disabled_border: (
red: 0.09411765,
green: 0.33333334,
blue: 0.16078432,
alpha: 0.5,
),
)

View File

@@ -0,0 +1,74 @@
(
base: (
red: 0.09411765,
green: 0.33333334,
blue: 0.16078432,
alpha: 1.0,
),
hover: (
red: 0.1530087,
green: 0.34438127,
blue: 0.20634201,
alpha: 1.0,
),
pressed: (
red: 0.41866177,
green: 0.53826964,
blue: 0.45199502,
alpha: 1.0,
),
selected: (
red: 0.1530087,
green: 0.34438127,
blue: 0.20634201,
alpha: 1.0,
),
selected_text: (
red: 0.23921569,
green: 0.04705882,
blue: 0.066666655,
alpha: 1.0,
),
focus: (
red: 0.23921569,
green: 0.04705882,
blue: 0.066666655,
alpha: 1.0,
),
divider: (
red: 0.8698165,
green: 0.8698165,
blue: 0.8698163,
alpha: 1.0,
),
on: (
red: 0.99999994,
green: 0.99999994,
blue: 0.9999997,
alpha: 1.0,
),
disabled: (
red: 0.09411765,
green: 0.33333334,
blue: 0.16078432,
alpha: 1.0,
),
on_disabled: (
red: 0.99999994,
green: 0.99999994,
blue: 0.9999997,
alpha: 0.5,
),
border: (
red: 0.09411765,
green: 0.33333334,
blue: 0.16078432,
alpha: 1.0,
),
disabled_border: (
red: 0.09411765,
green: 0.33333334,
blue: 0.16078432,
alpha: 0.5,
),
)

View File

@@ -0,0 +1,74 @@
(
base: (
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 0.0,
),
hover: (
red: 0.37074497,
green: 0.38277033,
blue: 0.46613896,
alpha: 0.2,
),
pressed: (
red: 0.7228412,
green: 0.7373518,
blue: 0.8313681,
alpha: 0.5,
),
selected: (
red: 0.37074497,
green: 0.38277033,
blue: 0.46613896,
alpha: 0.2,
),
selected_text: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
focus: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
divider: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 0.2,
),
on: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
disabled: (
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 0.0,
),
on_disabled: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 0.65,
),
border: (
red: 0.072988935,
green: 0.079405405,
blue: 0.14595589,
alpha: 1.0,
),
disabled_border: (
red: 0.072988935,
green: 0.079405405,
blue: 0.14595589,
alpha: 0.5,
),
)

View File

@@ -0,0 +1,5 @@
Some((
red: 0.17254902,
green: 0.20392157,
blue: 0.26666668,
))

View File

@@ -0,0 +1,74 @@
(
base: (
red: 0.3254902,
green: 0.28235295,
blue: 0.0,
alpha: 1.0,
),
hover: (
red: 0.33454114,
green: 0.30243644,
blue: 0.093227796,
alpha: 1.0,
),
pressed: (
red: 0.5241657,
green: 0.50985235,
blue: 0.41568404,
alpha: 1.0,
),
selected: (
red: 0.33454114,
green: 0.30243644,
blue: 0.093227796,
alpha: 1.0,
),
selected_text: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
focus: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
divider: (
red: 0.9999203,
green: 0.99998146,
blue: 1.0,
alpha: 1.0,
),
on: (
red: 0.9999203,
green: 0.99998146,
blue: 1.0,
alpha: 1.0,
),
disabled: (
red: 0.3254902,
green: 0.28235295,
blue: 0.0,
alpha: 1.0,
),
on_disabled: (
red: 0.66270524,
green: 0.6411672,
blue: 0.5,
alpha: 1.0,
),
border: (
red: 0.3254902,
green: 0.28235295,
blue: 0.0,
alpha: 1.0,
),
disabled_border: (
red: 0.3254902,
green: 0.28235295,
blue: 0.0,
alpha: 0.5,
),
)

View File

@@ -0,0 +1,74 @@
(
base: (
red: 0.3254902,
green: 0.28235295,
blue: 0.0,
alpha: 1.0,
),
hover: (
red: 0.33454114,
green: 0.30243644,
blue: 0.093227796,
alpha: 1.0,
),
pressed: (
red: 0.5241657,
green: 0.50985235,
blue: 0.41568404,
alpha: 1.0,
),
selected: (
red: 0.33454114,
green: 0.30243644,
blue: 0.093227796,
alpha: 1.0,
),
selected_text: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
focus: (
red: 0.9058823,
green: 0.6117647,
blue: 0.99607843,
alpha: 1.0,
),
divider: (
red: 0.0,
green: 0.0,
blue: 0.000000000000000000000000000000000000000000036,
alpha: 1.0,
),
on: (
red: 0.9999203,
green: 0.99998146,
blue: 1.0,
alpha: 1.0,
),
disabled: (
red: 0.3254902,
green: 0.28235295,
blue: 0.0,
alpha: 1.0,
),
on_disabled: (
red: 0.9999203,
green: 0.99998146,
blue: 1.0,
alpha: 0.5,
),
border: (
red: 0.3254902,
green: 0.28235295,
blue: 0.0,
alpha: 1.0,
),
disabled_border: (
red: 0.3254902,
green: 0.28235295,
blue: 0.0,
alpha: 0.5,
),
)

View File

@@ -0,0 +1 @@
None

View File

@@ -1,247 +1,122 @@
/* ── violet-chaton GTK3 theme — Nemo & GTK apps ───────────────────────────────
/* ── violet-chaton GTK theme (adw-gtk3-dark compatible) ────────────────────
*
* Couleurs extraites du thème COSMIC violet-chaton :
* bg #341C4A background.base
* surface #493161 background.component.base
* hover #5B4671 background.component.hover
* accent #E79CFE accent.base
* text #FCFCF6 background.on
* muted #7F849C neutral_7
* border #5C496C background.divider
* sidebar #2B1540 (bg légèrement plus sombre)
* Contenu identique au dark.css généré par COSMIC pour le thème violet-chaton.
* adw-gtk3-dark et libadwaita lisent ces variables @define-color.
* Sur le PC principal, COSMIC gère ce fichier via symlink — ce fichier
* sert de fallback lors de la première installation.
* ─────────────────────────────────────────────────────────────────────────── */
@define-color theme_bg_color #341C4A;
@define-color theme_fg_color #FCFCF6;
@define-color theme_base_color #493161;
@define-color theme_selected_bg_color #E79CFE;
@define-color theme_selected_fg_color #341C4A;
@define-color theme_text_color #FCFCF6;
@define-color borders #5C496C;
@define-color window_bg_color rgba(52, 28, 74, 1.00);
@define-color window_fg_color rgba(252, 252, 246, 1.00);
/* ── Fenêtre principale ────────────────────────────────────────────────────── */
window, .background {
background-color: #341C4A;
color: #FCFCF6;
}
@define-color view_bg_color rgba(56, 35, 75, 1.00);
@define-color view_fg_color rgba(193, 193, 187, 1.00);
/* ── Barre de titre / headerbar ───────────────────────────────────────────── */
headerbar, .titlebar {
background-color: #493161;
color: #FCFCF6;
border-bottom: 1px solid #5C496C;
}
@define-color headerbar_bg_color rgba(52, 28, 74, 1.00);
@define-color headerbar_fg_color rgba(252, 252, 246, 1.00);
@define-color headerbar_border_color_color rgba(92, 73, 108, 1.00);
@define-color headerbar_backdrop_color rgba(52, 28, 74, 1.00);
headerbar button, .titlebar button {
background-color: transparent;
color: #FCFCF6;
border: none;
border-radius: 6px;
}
@define-color sidebar_bg_color rgba(56, 35, 75, 1.00);
@define-color sidebar_fg_color rgba(193, 193, 187, 1.00);
@define-color sidebar_shade_color rgba(0, 0, 0, 0.08);
@define-color sidebar_backdrop_color rgba(72, 53, 89, 1.00);
headerbar button:hover, .titlebar button:hover {
background-color: #5B4671;
}
@define-color secondary_sidebar_bg_color rgba(69, 71, 90, 1.00);
@define-color secondary_sidebar_fg_color rgba(225, 225, 219, 1.00);
@define-color secondary_sidebar_shade_color rgba(0, 0, 0, 0.08);
@define-color secondary_sidebar_backdrop_color rgba(84, 86, 103, 1.00);
headerbar button:active, .titlebar button:active {
background-color: #E79CFE;
color: #341C4A;
}
@define-color card_bg_color rgba(73, 49, 97, 1.00);
@define-color card_fg_color rgba(212, 212, 206, 1.00);
/* ── Sidebar Nemo (paneau des emplacements) ───────────────────────────────── */
.sidebar, placessidebar {
background-color: #2B1540;
color: #FCFCF6;
border-right: 1px solid #5C496C;
}
@define-color thumbnail_bg_color rgba(73, 49, 97, 1.00);
@define-color thumbnail_fg_color rgba(212, 212, 206, 1.00);
.sidebar row, placessidebar row {
border-radius: 6px;
padding: 2px 4px;
}
@define-color dialog_bg_color rgba(56, 35, 75, 1.00);
@define-color dialog_fg_color rgba(193, 193, 187, 1.00);
.sidebar row:hover, placessidebar row:hover {
background-color: #5B4671;
}
@define-color popover_bg_color rgba(73, 49, 97, 1.00);
@define-color popover_fg_color rgba(212, 212, 206, 1.00);
.sidebar row:selected, placessidebar row:selected {
background-color: #E79CFE;
color: #341C4A;
}
@define-color shade_color rgba(0, 0, 0, 0.32);
@define-color scrollbar_outline_color rgba(52, 28, 74, 0.50);
.sidebar .sidebar-section-header, placessidebar .sidebar-section-header {
color: #7F849C;
font-size: smaller;
}
@define-color accent_color rgba(231, 156, 254, 1.00);
@define-color accent_bg_color rgba(231, 156, 254, 1.00);
@define-color accent_fg_color rgba(0, 0, 0, 1.00);
/* ── Vue fichiers (icônes + liste) ───────────────────────────────────────── */
.view, iconview, treeview {
background-color: #341C4A;
color: #FCFCF6;
}
@define-color destructive_color rgba(243, 139, 168, 1.00);
@define-color destructive_bg_color rgba(243, 139, 168, 1.00);
@define-color destructive_fg_color rgba(0, 0, 0, 1.00);
.view:selected, iconview:selected,
treeview:selected, .view:focus:selected {
background-color: #E79CFE;
color: #341C4A;
}
@define-color warning_color rgba(249, 226, 175, 1.00);
@define-color warning_bg_color rgba(249, 226, 175, 1.00);
@define-color warning_fg_color rgba(0, 0, 0, 1.00);
/* En-têtes de colonnes (vue liste) */
treeview header button {
background-color: #493161;
color: #FCFCF6;
border: none;
border-right: 1px solid #5C496C;
border-bottom: 1px solid #5C496C;
}
@define-color success_color rgba(166, 227, 161, 1.00);
@define-color success_bg_color rgba(166, 227, 161, 1.00);
@define-color success_fg_color rgba(0, 0, 0, 1.00);
treeview header button:hover {
background-color: #5B4671;
}
@define-color accent_color rgba(231, 156, 254, 1.00);
@define-color accent_bg_color rgba(231, 156, 254, 1.00);
@define-color accent_fg_color rgba(0, 0, 0, 1.00);
/* ── Barre d'outils / pathbar ─────────────────────────────────────────────── */
toolbar, .path-bar {
background-color: #493161;
border-bottom: 1px solid #5C496C;
}
@define-color error_color rgba(243, 139, 168, 1.00);
@define-color error_bg_color rgba(243, 139, 168, 1.00);
@define-color error_fg_color rgba(0, 0, 0, 1.00);
.path-bar button {
background-color: transparent;
color: #FCFCF6;
border: none;
border-radius: 6px;
}
@define-color blue_1 rgba(151, 195, 255, 1.00);
@define-color blue_2 rgba(144, 187, 255, 1.00);
@define-color blue_3 rgba(137, 180, 250, 1.00);
@define-color blue_4 rgba(114, 156, 224, 1.00);
@define-color blue_5 rgba(91, 132, 199, 1.00);
.path-bar button:hover {
background-color: #5B4671;
}
@define-color green_1 rgba(175, 236, 170, 1.00);
@define-color green_2 rgba(171, 232, 165, 1.00);
@define-color green_3 rgba(166, 227, 161, 1.00);
@define-color green_4 rgba(139, 199, 134, 1.00);
@define-color green_5 rgba(113, 171, 108, 1.00);
.path-bar button:checked {
background-color: #5B4671;
color: #E79CFE;
}
@define-color yellow_1 rgba(254, 231, 180, 1.00);
@define-color yellow_2 rgba(252, 229, 178, 1.00);
@define-color yellow_3 rgba(249, 226, 175, 1.00);
@define-color yellow_4 rgba(219, 196, 146, 1.00);
@define-color yellow_5 rgba(189, 167, 118, 1.00);
/* ── Barre de recherche / entrée texte ────────────────────────────────────── */
entry {
background-color: #5B4671;
color: #FCFCF6;
border: 1px solid #5C496C;
border-radius: 6px;
padding: 4px 8px;
}
@define-color red_1 rgba(255, 154, 183, 1.00);
@define-color red_2 rgba(252, 147, 176, 1.00);
@define-color red_3 rgba(243, 139, 168, 1.00);
@define-color red_4 rgba(217, 116, 145, 1.00);
@define-color red_5 rgba(191, 93, 122, 1.00);
entry:focus {
border-color: #E79CFE;
box-shadow: 0 0 0 2px alpha(#E79CFE, 0.3);
}
@define-color orange_1 rgba(255, 190, 146, 1.00);
@define-color orange_2 rgba(255, 185, 140, 1.00);
@define-color orange_3 rgba(250, 179, 135, 1.00);
@define-color orange_4 rgba(222, 153, 110, 1.00);
@define-color orange_5 rgba(195, 128, 85, 1.00);
entry placeholder {
color: #7F849C;
}
@define-color purple_1 rgba(192, 202, 255, 1.00);
@define-color purple_2 rgba(186, 196, 255, 1.00);
@define-color purple_3 rgba(180, 190, 254, 1.00);
@define-color purple_4 rgba(155, 164, 226, 1.00);
@define-color purple_5 rgba(130, 139, 200, 1.00);
@define-color light_0 rgba(0, 0, 0, 1.00);
@define-color light_1 rgba(3, 3, 16, 1.00);
@define-color light_2 rgba(24, 25, 43, 1.00);
@define-color light_3 rgba(50, 53, 72, 1.00);
@define-color light_4 rgba(79, 82, 103, 1.00);
@define-color dark_0 rgba(110, 114, 135, 1.00);
@define-color dark_1 rgba(143, 147, 169, 1.00);
@define-color dark_2 rgba(177, 181, 205, 1.00);
@define-color dark_3 rgba(213, 217, 241, 1.00);
@define-color dark_4 rgba(255, 255, 255, 1.00);
/* ── Scrollbar ────────────────────────────────────────────────────────────── */
scrollbar {
background-color: transparent;
}
scrollbar slider {
background-color: #5C496C;
border-radius: 10px;
min-width: 6px;
min-height: 6px;
}
scrollbar slider:hover {
background-color: #E79CFE;
}
/* ── Menu contextuel ──────────────────────────────────────────────────────── */
menu, .context-menu, .popup {
background-color: #493161;
color: #FCFCF6;
border: 1px solid #5C496C;
border-radius: 8px;
padding: 4px;
}
menuitem {
border-radius: 6px;
padding: 4px 8px;
}
menuitem:hover {
background-color: #E79CFE;
color: #341C4A;
}
menuitem accelerator {
color: #7F849C;
}
separator, menuitem separator {
background-color: #5C496C;
min-height: 1px;
}
/* ── Barre de statut ──────────────────────────────────────────────────────── */
.statusbar, statusbar {
background-color: #493161;
color: #7F849C;
border-top: 1px solid #5C496C;
font-size: smaller;
}
/* ── Boutons génériques ───────────────────────────────────────────────────── */
button {
background-color: #5B4671;
color: #FCFCF6;
border: 1px solid #5C496C;
border-radius: 6px;
padding: 4px 12px;
}
button:hover {
background-color: #E79CFE;
color: #341C4A;
border-color: #E79CFE;
}
button.suggested-action {
background-color: #E79CFE;
color: #341C4A;
border-color: #E79CFE;
}
button.destructive-action {
background-color: #F38BA8;
color: #341C4A;
}
/* ── Notebooks / onglets ─────────────────────────────────────────────────── */
notebook tab {
background-color: #493161;
color: #7F849C;
border-radius: 6px 6px 0 0;
padding: 4px 12px;
}
notebook tab:checked {
background-color: #5B4671;
color: #FCFCF6;
}
/* ── Popover ─────────────────────────────────────────────────────────────── */
popover {
background-color: #493161;
color: #FCFCF6;
border: 1px solid #5C496C;
border-radius: 8px;
}
/* ── Tooltip ─────────────────────────────────────────────────────────────── */
tooltip {
background-color: #5B4671;
color: #FCFCF6;
border-radius: 6px;
padding: 4px 8px;
}
/* ── Variables GTK3 classiques (compat apps legacy) ─────────────────────── */
@define-color theme_bg_color rgba(52, 28, 74, 1.00);
@define-color theme_fg_color rgba(252, 252, 246, 1.00);
@define-color theme_base_color rgba(73, 49, 97, 1.00);
@define-color theme_selected_bg_color rgba(231, 156, 254, 1.00);
@define-color theme_selected_fg_color rgba(52, 28, 74, 1.00);
@define-color theme_text_color rgba(252, 252, 246, 1.00);
@define-color borders rgba(92, 73, 108, 1.00);

View File

@@ -0,0 +1,137 @@
/* ── violet-chaton rofi theme ─────────────────────────────────────────────── */
* {
bg0: #261537;
bg1: #341c4a;
surface: #493161;
pink: #ff79c6;
purple: #e79cfe;
cyan: #8be9fd;
text: #f8f8f2;
muted: #6c7086;
overlay: #9399b2;
background-color: transparent;
text-color: @text;
}
/* ── Fenêtre — positionnée sous les pills ─────────────────────────────────── */
window {
background-color: rgba(38, 21, 55, 0.94);
border: 2px;
border-color: rgba(255, 121, 198, 0.38);
border-radius: 14px;
width: 500px;
padding: 0px;
}
/* ── Layout — résultats en haut, recherche en bas ─────────────────────────── */
mainbox {
background-color: transparent;
padding: 10px;
spacing: 0;
children: [ listview, inputbar ];
}
/* ── Liste des résultats ──────────────────────────────────────────────────── */
listview {
background-color: transparent;
padding: 4px 0 8px 0;
spacing: 2px;
lines: 9;
columns: 1;
scrollbar: false;
dynamic: false;
}
element {
background-color: transparent;
border-radius: 8px;
padding: 7px 10px;
spacing: 10px;
orientation: horizontal;
}
element selected {
background-color: rgba(255, 121, 198, 0.16);
border: 1px;
border-color: rgba(255, 121, 198, 0.32);
}
element hover {
background-color: rgba(255, 121, 198, 0.16);
border: 1px;
border-color: rgba(255, 121, 198, 0.32);
}
element-icon {
size: 22px;
background-color: transparent;
}
element-text {
background-color: transparent;
text-color: @text;
vertical-align: 0.5;
highlight: bold;
}
element-text selected {
text-color: @pink;
}
element-text hover {
text-color: @pink;
}
/* ── Séparateur visuel avant la recherche ─────────────────────────────────── */
inputbar {
background-color: rgba(52, 28, 74, 0.75);
border-radius: 0 0 12px 12px;
border: 1px;
border-color: rgba(92, 73, 108, 0.50);
padding: 9px 14px;
spacing: 8px;
children: [ prompt, entry ];
}
prompt {
background-color: transparent;
text-color: @pink;
font: "JetBrainsMono Nerd Font Bold 14";
vertical-align: 0.5;
}
entry {
background-color: transparent;
text-color: @text;
placeholder: "Rechercher une application...";
placeholder-color: @muted;
vertical-align: 0.5;
}
/* ── Scrollbar ────────────────────────────────────────────────────────────── */
scrollbar {
background-color: rgba(92, 73, 108, 0.30);
handle-color: rgba(255, 121, 198, 0.40);
handle-width: 4px;
border-radius: 2px;
padding: 0;
}
/* ── Messages ─────────────────────────────────────────────────────────────── */
message {
background-color: transparent;
padding: 4px 8px;
}
textbox {
text-color: @overlay;
background-color: transparent;
}

View File

@@ -0,0 +1,419 @@
/* ── violet-chaton Waybar — island floating 3 pills glassmorphism ───────────
*
* Palette :
* bg-medium rgba(52, 28, 74, 0.88) #341c4a
* surface rgba(73, 49, 97, 0.95) #493161
* pink #ff79c6 accent primaire
* purple #e79cfe accent secondaire
* cyan #8be9fd info
* text #f8f8f2 texte principal
* muted #6c7086 texte secondaire
* warning #f9e2af
* danger #f38ba8
* success #a6e3a1
* border #5C496C
*
* ─────────────────────────────────────────────────────────────────────────── */
/* ── Reset global ─────────────────────────────────────────────────────────── */
* {
font-family: "JetBrainsMono Nerd Font", "JetBrains Mono", monospace;
font-size: 13px;
font-weight: bold;
min-height: 0;
border: none;
border-radius: 0;
transition: all 0.2s ease;
}
/* ── Fenêtre — fond transparent entre les pills ───────────────────────────── */
window#waybar {
background: transparent;
color: #f8f8f2;
}
/* ── Pills (glassmorphism) ────────────────────────────────────────────────── */
.modules-left,
.modules-center,
.modules-right {
background: rgba(52, 28, 74, 0.88);
border-radius: 14px;
border: 3px solid rgba(255, 121, 198, 0.78);
margin: 8px 2px 8px 2px;
}
/* Hover pill glow */
.modules-left:hover,
.modules-center:hover,
.modules-right:hover {
border-color: #ff79c6;
box-shadow:
0 4px 28px rgba(231, 156, 254, 0.18),
0 1px 0 rgba(255, 255, 255, 0.06) inset;
}
/* ── Animations ───────────────────────────────────────────────────────────── */
@keyframes pulse-critical {
0% { color: #f38ba8; }
50% { color: rgba(243, 139, 168, 0.35); }
100% { color: #f38ba8; }
}
@keyframes pulse-warning {
0% { color: #f9e2af; }
50% { color: rgba(249, 226, 175, 0.50); }
100% { color: #f9e2af; }
}
@keyframes glow-pink {
0% { box-shadow: 0 0 6px rgba(255, 121, 198, 0.4); }
50% { box-shadow: 0 0 14px rgba(255, 121, 198, 0.8); }
100% { box-shadow: 0 0 6px rgba(255, 121, 198, 0.4); }
}
/* ── Launcher ─────────────────────────────────────────────────────────────── */
#custom-launcher {
color: #ff79c6;
font-size: 19px;
padding: 0 14px 0 18px;
border-radius: 14px 0 0 14px;
}
#custom-launcher:hover {
color: #e79cfe;
animation: glow-pink 1.4s ease-in-out infinite;
}
/* ── Séparateur ───────────────────────────────────────────────────────────── */
#custom-sep {
color: rgba(248, 248, 242, 0.12);
font-size: 11px;
padding: 0 4px;
font-weight: normal;
}
/* ── Base commune modules ─────────────────────────────────────────────────── */
#cpu,
#temperature,
#custom-gpu,
#memory,
#disk,
#custom-network,
#clock,
#custom-date,
#mpris,
#custom-cava,
#wireplumber,
#backlight,
#bluetooth,
#idle-inhibitor,
#battery,
#custom-power-profile,
#custom-uptime,
#tray {
padding: 0 8px;
color: #f8f8f2;
background-color: transparent;
}
/* ── CPU ──────────────────────────────────────────────────────────────────── */
#cpu {
color: #8be9fd;
}
#cpu.warning {
color: #f9e2af;
}
#cpu.critical {
color: #f38ba8;
animation: pulse-critical 0.8s linear infinite;
}
/* ── Température ──────────────────────────────────────────────────────────── */
#temperature {
color: rgba(139, 233, 253, 0.60);
font-size: 11px;
font-weight: normal;
padding-left: 2px;
padding-right: 10px;
}
#temperature.critical {
color: #f38ba8;
font-weight: bold;
animation: pulse-critical 0.8s linear infinite;
}
/* ── GPU ──────────────────────────────────────────────────────────────────── */
#custom-gpu {
color: #ff79c6;
}
#custom-gpu.warning {
color: #f9e2af;
}
#custom-gpu.critical {
color: #f38ba8;
animation: pulse-critical 0.8s linear infinite;
}
/* ── RAM ──────────────────────────────────────────────────────────────────── */
#memory {
color: #ff79c6;
}
#memory.warning {
color: #f9e2af;
}
#memory.critical {
color: #f38ba8;
animation: pulse-critical 0.8s linear infinite;
}
/* ── Disque ───────────────────────────────────────────────────────────────── */
#disk {
color: rgba(255, 121, 198, 0.70);
font-size: 11px;
font-weight: normal;
}
/* ── Réseau ───────────────────────────────────────────────────────────────── */
#custom-network {
color: #8be9fd;
font-size: 11px;
font-weight: normal;
padding-right: 14px;
}
#custom-network.disconnected {
color: #f38ba8;
}
#custom-network.eth {
color: #8be9fd;
}
#custom-network.wifi {
color: rgba(139, 233, 253, 0.80);
}
/* ── CAVA ─────────────────────────────────────────────────────────────────── */
#custom-cava {
color: rgba(231, 156, 254, 0.65);
font-size: 11px;
letter-spacing: 1px;
padding: 0 10px;
font-weight: normal;
}
/* ── Horloge ──────────────────────────────────────────────────────────────── */
#clock {
color: #ff79c6;
font-weight: 900;
font-size: 14px;
letter-spacing: 0.04em;
padding: 0 10px;
}
#clock:hover {
color: #e79cfe;
}
/* ── Date ─────────────────────────────────────────────────────────────────── */
#custom-date {
color: #8be9fd;
font-size: 12px;
font-weight: normal;
padding: 0 10px 0 2px;
letter-spacing: 0.03em;
}
/* ── MPRIS ────────────────────────────────────────────────────────────────── */
#mpris {
color: #e79cfe;
font-size: 12px;
font-weight: normal;
padding: 0 10px;
}
#mpris.paused {
color: rgba(231, 156, 254, 0.50);
font-style: italic;
}
/* ── Volume (wireplumber) ─────────────────────────────────────────────────── */
#wireplumber {
color: #ff79c6;
}
#wireplumber.muted {
color: rgba(255, 121, 198, 0.30);
}
/* ── Luminosité ───────────────────────────────────────────────────────────── */
#backlight {
color: #8be9fd;
}
/* ── Bluetooth ────────────────────────────────────────────────────────────── */
#bluetooth {
color: rgba(139, 233, 253, 0.45);
font-size: 15px;
}
#bluetooth.connected {
color: #e79cfe;
}
#bluetooth.disabled {
color: rgba(108, 112, 134, 0.50);
}
/* ── Idle inhibitor ───────────────────────────────────────────────────────── */
#idle-inhibitor {
color: rgba(248, 248, 242, 0.35);
font-size: 14px;
}
#idle-inhibitor.activated {
color: #f9e2af;
animation: pulse-warning 1.6s ease-in-out infinite;
}
/* ── Batterie ─────────────────────────────────────────────────────────────── */
#battery {
color: #ff79c6;
}
#battery.charging,
#battery.plugged {
color: #a6e3a1;
}
#battery.full {
color: rgba(166, 227, 161, 0.65);
}
#battery.warning:not(.charging):not(.plugged) {
color: #f9e2af;
}
#battery.critical:not(.charging):not(.plugged) {
color: #f38ba8;
animation: pulse-critical 0.6s linear infinite;
}
/* ── Profil énergie ───────────────────────────────────────────────────────── */
#custom-power-profile {
color: #8be9fd;
font-size: 15px;
padding: 0 6px;
}
#custom-power-profile.performance {
color: #f38ba8;
}
#custom-power-profile.balanced {
color: #8be9fd;
}
#custom-power-profile.low-power {
color: #a6e3a1;
}
/* ── Uptime ───────────────────────────────────────────────────────────────── */
#custom-uptime {
color: rgba(139, 233, 253, 0.45);
font-size: 11px;
font-weight: normal;
}
/* ── Tray ─────────────────────────────────────────────────────────────────── */
#tray {
padding: 0 8px;
}
/* ── Power ────────────────────────────────────────────────────────────────── */
#custom-power {
color: #f38ba8;
font-size: 15px;
padding: 0 14px 0 8px;
border-radius: 0 14px 14px 0;
}
#custom-power:hover {
color: #ff79c6;
}
#tray > .passive {
-gtk-icon-effect: dim;
}
#tray > .needs-attention {
-gtk-icon-effect: highlight;
background-color: rgba(255, 121, 198, 0.12);
border-radius: 6px;
}
/* ── Tooltip ──────────────────────────────────────────────────────────────── */
tooltip {
background-color: rgba(38, 21, 55, 0.96);
border: 1px solid rgba(255, 121, 198, 0.30);
border-radius: 10px;
color: #f8f8f2;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.40);
}
tooltip label {
color: #f8f8f2;
padding: 3px;
}
/* ── Hover global ─────────────────────────────────────────────────────────── */
#cpu:hover,
#memory:hover,
#disk:hover,
#custom-network:hover,
#wireplumber:hover,
#backlight:hover,
#battery:hover,
#custom-power-profile:hover,
#custom-uptime:hover {
color: #e79cfe;
}
#bluetooth:hover {
color: #e79cfe;
}

View File

@@ -0,0 +1,54 @@
/* ── violet-chaton wofi — power menu (sans barre de recherche) ─────────────── */
window {
background-color: rgba(52, 28, 74, 0.88);
border: 2px solid rgba(255, 121, 198, 0.28);
border-radius: 14px;
font-family: "JetBrainsMono Nerd Font";
font-size: 13px;
font-weight: bold;
}
/* Cacher complètement la barre de recherche */
#input {
min-height: 0;
min-width: 0;
padding: 0;
margin: 0;
border: 0;
opacity: 0;
}
#outer-box {
padding: 0;
border-radius: 14px;
}
#scroll {
margin: 6px 0;
}
#inner-box {
padding: 4px 6px;
}
#entry {
border-radius: 8px;
padding: 9px 14px;
margin: 1px 0;
border: 1px solid transparent;
}
#entry:selected {
background-color: rgba(243, 139, 168, 0.16);
border-color: rgba(243, 139, 168, 0.35);
}
#text {
color: #f8f8f2;
font-family: "JetBrainsMono Nerd Font";
}
#text:selected {
color: #f38ba8;
}

View File

@@ -0,0 +1,64 @@
/* ── violet-chaton wofi theme ─────────────────────────────────────────────── */
window {
background-color: rgba(52, 28, 74, 0.88);
border: 2px solid rgba(255, 121, 198, 0.28);
border-radius: 14px;
font-family: "JetBrainsMono Nerd Font";
font-size: 13px;
font-weight: bold;
}
/* ── Barre de recherche ──────────────────────────────────────────────────── */
#input {
all: unset;
background-color: rgba(38, 21, 55, 0.60);
color: #f8f8f2;
border-bottom: 1px solid rgba(92, 73, 108, 0.50);
border-radius: 12px 12px 0 0;
padding: 10px 14px;
margin: 0;
font-family: "JetBrainsMono Nerd Font";
font-size: 13px;
font-weight: bold;
caret-color: #ff79c6;
}
/* ── Conteneurs ──────────────────────────────────────────────────────────── */
#outer-box {
padding: 0;
border-radius: 14px;
}
#scroll {
margin: 4px 0;
}
#inner-box {
padding: 4px 6px;
}
/* ── Entrées ─────────────────────────────────────────────────────────────── */
#entry {
border-radius: 8px;
padding: 7px 10px;
margin: 1px 0;
border: 1px solid transparent;
}
#entry:selected {
background-color: rgba(255, 121, 198, 0.16);
border-color: rgba(255, 121, 198, 0.32);
}
#text {
color: #f8f8f2;
font-family: "JetBrainsMono Nerd Font";
}
#text:selected {
color: #ff79c6;
}

View File

@@ -63,6 +63,15 @@ Installe les outils via le gestionnaire de paquets système :
| `w3m` | Navigateur web en mode texte dans le terminal |
| `jp2a` | Conversion d'images JPEG/PNG en ASCII art |
| `qalc` | Calculatrice CLI — unités, conversions, expressions complexes |
| `waybar` | Barre de statut Wayland 3-pills glassmorphism |
| `wob` | Overlay volume/luminosité animé |
| `wofi` | Launcher d'applications et menu power |
| `brightnessctl` | Contrôle de la luminosité rétroéclairage |
| `playerctl` | Contrôle MPRIS (lecture/pause, titre en cours) |
| `wireplumber` | Gestionnaire audio PipeWire (`wpctl`) |
| `python3-gi` | Bindings Python GTK3 (popups volume/luminosité) |
| `gir1.2-gtk-3.0` | Introspection GTK3 pour Python |
| `gir1.2-gtklayershell-0.1` | Layer-shell Wayland pour popups GTK flottants |
Définit aussi **zsh comme shell par défaut** via `chsh`.
@@ -139,10 +148,31 @@ Le chemin exact est affiché à la fin du script si des fichiers ont été sauve
**COSMIC Desktop (entièrement automatique) :**
- `~/.config/cosmic/com.system76.CosmicTheme.Dark/v1/` — palette violet-chaton complète
- `~/.config/cosmic/com.system76.CosmicTheme.Light/v1/` — palette violet-chaton (mode clair)
- `~/.config/cosmic/com.system76.CosmicTheme.Mode/v1/is_dark` — mode sombre activé
- `~/.config/cosmic/com.system76.CosmicTerm/v1/` — police JetBrains Mono, couleurs, profil
- `~/.config/cosmic/com.system76.CosmicTk/v1/` — icônes candy-icons, polices UI 0xProto
**Waybar — island floating 3 pills :**
- `~/.config/waybar/config` — modules left/center/right
- `~/.config/waybar/style.css` — glassmorphism, bordures roses, animations
- `~/.config/waybar/cava-waybar.cfg` — config CAVA dédié waybar
- `~/.config/waybar/scripts/` — tous les scripts (gpu, network, power-profile, cava, wob, popups GTK)
- `~/.config/autostart/waybar.desktop` — démarrage automatique avec COSMIC
- `~/.config/autostart/wob.desktop` — démarrage automatique de l'overlay wob
**Wofi — launcher + menu power :**
- `~/.config/wofi/config` — configuration wofi
- `~/.config/wofi/style.css` — thème violet-chaton (launcher apps)
- `~/.config/wofi/power-style.css` — thème violet-chaton (menu power)
**wob — overlay volume/luminosité :**
- `~/.config/wob.ini` — couleurs violet-chaton, position bas de l'écran
**Système (avec sudo) :**
- `/etc/sudoers.d/waybar-power-profile` — changement de profil énergie sans mot de passe
- `/etc/udev/rules.d/90-platform-profile.rules` — permissions groupe `video` sur platform_profile
**Vivaldi (avec pause de sécurité) :**
- Si Vivaldi n'a pas encore été lancé, le script s'arrête et demande de le démarrer une fois
- Le thème **Rice Violet-Chaton** est ensuite injecté directement dans `~/.config/vivaldi/Default/Preferences`
@@ -226,6 +256,28 @@ gtk-update-icon-cache ~/.local/share/icons/candy-icons-master
gsettings set org.gnome.desktop.interface icon-theme 'candy-icons-master'
```
### Waybar ne démarre pas
```bash
waybar & # tester manuellement, lire les erreurs
pkill -SIGUSR2 waybar # recharger la config à chaud
```
### Popups volume/luminosité ne s'ouvrent pas
Vérifier que les dépendances Python sont installées :
```bash
python3 -c "import gi; gi.require_version('GtkLayerShell', '0.1'); print('OK')"
```
### Profil énergie ne change pas au clic
Vérifier que la règle sudoers et les permissions udev sont en place :
```bash
ls -la /sys/firmware/acpi/platform_profile # doit être g+w groupe video
cat /etc/sudoers.d/waybar-power-profile
```
---
## Raccourcis configurés
@@ -283,7 +335,18 @@ INSTALL/
│ ├── atuin.toml
│ ├── lazygit.yml
│ ├── yazi.toml
── glow.yml
── glow.yml
│ ├── autostart/
│ │ ├── waybar.desktop démarrage automatique waybar
│ │ └── wob.desktop démarrage automatique wob
│ ├── waybar/
│ │ ├── config modules 3-pills
│ │ ├── cava-waybar.cfg config CAVA dédiée waybar
│ │ └── scripts/ gpu, network, power-profile, cava, wob, popups GTK
│ ├── wofi/
│ │ └── config config wofi
│ └── wob/
│ └── wob.ini overlay volume/luminosité
├── assets/
│ └── violet-chaton-logo.png logo fastfetch (1024×1024)
└── themes/ tous les fichiers de thème violet-chaton
@@ -296,8 +359,12 @@ INSTALL/
├── violet-chaton-ls-colors.sh
├── violet-chaton-vivaldi.json thème Rice Violet-Chaton pour Vivaldi
├── violet-chaton.theme.css thème Discord/Vesktop compilé
├── violet-chaton-waybar.css CSS 3-pills glassmorphism
├── violet-chaton-wofi.css thème wofi launcher
├── violet-chaton-wofi-power.css thème wofi menu power
├── cosmic/ configs COSMIC déployées automatiquement
│ ├── com.system76.CosmicTheme.Dark/v1/
│ ├── com.system76.CosmicTheme.Light/v1/
│ ├── com.system76.CosmicTheme.Mode/v1/
│ ├── com.system76.CosmicTerm/v1/
│ └── com.system76.CosmicTk/v1/
@@ -317,6 +384,16 @@ cp ~/.config/starship.toml ~/Documents/config-violet-chaton/INSTALL/configs/star
# Mettre à jour les configs COSMIC
cp ~/.config/cosmic/com.system76.CosmicTerm/v1/* \
~/Documents/config-violet-chaton/INSTALL/themes/cosmic/com.system76.CosmicTerm/v1/
# Mettre à jour la config ou le CSS waybar
cp ~/.config/waybar/config \
~/Documents/config-violet-chaton/INSTALL/configs/waybar/config
cp ~/.config/waybar/style.css \
~/Documents/config-violet-chaton/INSTALL/themes/violet-chaton-waybar.css
# Mettre à jour un script waybar
cp ~/.config/waybar/scripts/power-profile.sh \
~/Documents/config-violet-chaton/INSTALL/configs/waybar/scripts/power-profile.sh
```
---