Compare commits

..

17 Commits

Author SHA1 Message Date
29b4c54370 feat(ags-v3): brain HUD terminal — kitty toggle + single instance + COSMIC window rule
- BrainPower panel: dashboard only (VTE embed pas compatible AGS JSX)
- toggle-brain.sh: ouvre/ferme panel AGS + terminal Kitty (single instance via pgrep)
- Kitty class brain-hud-terminal pour COSMIC window rule (floating)
- app.ts: cleanup focusBrainTerm removed
- style.scss: brain-terminal class + brain-commits-list
2026-03-26 15:54:49 +01:00
9eaaa01663 feat(ags-v3): desktop adaptation — ultrawide scaling, brain power panel, system stats
- Scaling 16px base pour ultrawide 3440x1440
- Bar: CPU/RAM/GPU visible, media single player (skip playerctld), network tooltip LAN/WAN IPv4
- Volume: class module pour sizing cohérent
- Battery: désactivé (PC fixe)
- Clock: tooltip calendrier + uptime
- BrainPower: panel enrichi (focus, session, intentions, todos, repos git, derniers commits)
- App: BrainPower sur moniteur principal uniquement
- Heartbeat: Layer.TOP pour compatibilité COSMIC
2026-03-26 15:25:03 +01:00
e94b841b2a Merge pull request 'feat: Giga Rice 2026 — violet-chaton v2 + Ghost Shell AGS v3' (#1) from features/giga-rice-2026 into dev
Reviewed-on: #1
2026-03-26 08:14:28 +00:00
51b725e1f7 fix(ags-v3): nice-to-have — portabilité, perf, réactivité
- Prompt.tsx: GLib.get_user_name() au lieu de hardcode "tetardtek"
- ghost-shell.desktop: $HOME au lieu de chemin absolu
- SystemStats.tsx: lecture /proc/stat + /proc/meminfo (zero fork, économie batterie)
- Battery.tsx: createDerivedBinding percentage+charging — réactif sur branchement
2026-03-26 09:13:36 +01:00
da22b6446d fix(ags-v3): audit fixes — portable brain path, reactive volume, SCSS dedup
- brain.ts: BRAIN_ROOT résolu via $BRAIN_ROOT env / ~/.config/brain-path / fallback ~/Dev/Brain
- Volume.tsx: bindings volume + mute séparés et réactifs
- style.scss: importe _bar.scss et _heartbeat.scss via @use, supprime 199 lignes dupliquées
2026-03-26 08:58:33 +01:00
Tetardtek-Cortex
932b174c36 feat: Ghost Shell v2 — AGS v3 statusbar + violet-chaton v2 palette
- AGS v3.1.0 (Astal/GTK3) Ghost Shell avec ghost mode (heartbeat + hover reveal)
- Modules : clock, battery, volume (interactif), network, MPRIS, CPU/RAM, systray
- Brain Power panel (Super + B) — lecture live focus/todos/session
- tetardtek_ prompt avec curseur clignotant
- Palette violet-chaton v2 documentée (Mitsuri Kanroji gradient magenta → green)
- Autostart COSMIC via .desktop
- Archive AGS v1 conservée pour référence
2026-03-26 06:54:17 +01:00
Tetardtek-Cortex
7e9d12e640 feat: violet-chaton v2 — palette originale, kitty, AGS, Maple Mono NF
Refonte complete du rice. Palette 100% originale (Mitsuri Kanroji inspired),
zero emprunt Dracula/Catppuccin. 50 fichiers, 3200+ lignes.

Palette v2:
- palette.sh source de verite unique (dark + light)
- 5 accents (magenta, lilac, mitsuri, lavande, champagne)
- 4 semantiques derivees, 4 niveaux texte, 6 fonds
- Gradient signature: magenta → lilac → lavande → mitsuri
- Variante Light: fonds lavande, accents assombris WCAG

Terminal:
- kitty (remplace COSMIC Term comme principal)
- Maple Mono NF (cursive italics, ligatures)
- Cursor trail magenta, splits/layouts tiling, undercurl
- Vi-mode zsh avec cursor shape adaptatif

Shell:
- starship 3 lignes (palette nommee, brain_name, battery, sudo)
- zshrc v2 (nouveaux outils, fzf pimp, shell functions, vi-mode)
- Commandes custom: proj, glog, fkill, colors, hotkeys, weather, y

Desktop:
- AGS config (bar 3-pills, OSD gradient, launcher, notifications)
- COSMIC Dark + Light v2 (7 fichiers RON chacun)
- COSMIC Term v2 (color schemes dark/light, Maple Mono NF)
- GTK3/GTK4 dark + light css
- Vivaldi theme v2

Outils:
- +kitty +dust +procs +tokei +sd +hyperfine +gping +Maple Mono NF
- Propagation palette sur: bat, btop, cava, yazi, lazygit, rofi,
  delta, fastfetch, atuin, ls-colors, vivaldi
- Claude Code statusline brain-aware

Docs:
- README v2 complet (palette, structure, raccourcis, commandes)
- help.md v2 (reference exhaustive)
2026-03-26 03:57:18 +01:00
7d4d54c5b8 docs(readme): réécriture — rice compatible COSMIC + Hyprland, léger et cohérent 2026-03-16 01:34:07 +01:00
Tetardtek
f0adb2a291 Merge features/media-panel → main 2026-02-23 22:17:25 +01:00
Tetardtek
01133b78f0 feat(media-popup): intégrer contrôleur MPRIS avec album art et contrôles
Section LECTURE apparaît automatiquement si un lecteur est actif (playerctl).
- Album art chargé en arrière-plan (file:// et http/https), coins arrondis
  dessinés en Cairo avec fallback icône note de musique
- Contrôles prev / play-pause / next via playerctl
- Titre et artiste avec ellipsis, rafraîchis toutes les 2s
- Suppression de vc-brightness-popup.py et vc-volume-popup.py (fusionnés)
Dépendances apt ajoutées : gir1.2-gdkpixbuf-2.0, gir1.2-pango-1.0
2026-02-23 22:16:57 +01:00
Tetardtek
50f84afb66 fix(waybar/power-profile): masquer le module sur PC sans ACPI platform_profile
Retourne un JSON avec text vide et class 'unavailable' si
/sys/firmware/acpi/platform_profile est absent (PC fixe, VM),
évitant l'affichage d'une erreur dans la barre.
2026-02-23 22:16:47 +01:00
Tetardtek
59c39260d1 fix(waybar/network): détecter l'interface active via la route par défaut
Remplace la liste codée en dur d'interfaces (enp7s0, wlp8s0…) par
'ip route get 1.1.1.1' qui retourne l'interface réellement utilisée,
portable sur n'importe quelle machine sans configuration.
2026-02-23 22:16:41 +01:00
Tetardtek
0ba6bbd181 feat(waybar): modules cpu-temp et disks portables par scripts
Remplace les modules natifs temperature (thermal-zone codé en dur) et
disk (chemin fixe /) par des custom scripts auto-détectés.
- cpu-temp.sh : détecte x86_pkg_temp / k10temp / coretemp via thermal_zone
  et hwmon, émet warning à 65° et critical à 80°
- disks.sh : liste tous les FS montés réels, exclut snap/tmpfs/efi,
  affiche icône selon le point de montage, tooltip détaillé
CSS : styles warning + hover ajoutés pour les deux modules
2026-02-23 22:16:05 +01:00
Tetardtek
1690ec5eb4 fix(waybar/autostart): empêcher le double démarrage via flock
Sur Pop!_OS 24.04, systemd-xdg-autostart-generator et cosmic-session
traitent tous deux ~/.config/autostart/ → deux instances waybar.
Utilise flock sur un fichier verrou /tmp pour n'en démarrer qu'une.
2026-02-23 22:14:51 +01:00
Tetardtek
2f3fc71ab7 feat(media-panel): sélecteur sortie/entrée audio
- Sélecteur de sortie (SORTIE) : liste verticale des sinks disponibles,
  filtre les SUSPENDED (HDMI non branchés), actif surligné en rose
- Sélecteur d'entrée (ENTRÉE) : même logique, filtre les .monitor
  (loopbacks), garde les vrais micros même SUSPENDED
- Popup ancré à droite (plus jamais hors écran)
- LANG=C pour pactl (indépendant de la locale système)
2026-02-23 19:35:10 +01:00
Tetardtek
40850161a5 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.
2026-02-23 19:02:31 +01:00
Tetardtek
8ee25d7853 fix(install): corriger le thème GTK sur fresh install
- Ajouter adw-gtk3 aux paquets apt (base du dark theme GTK3)
- Déployer violet-chaton-gtk.css sur gtk-3.0 ET gtk-4.0
- Appliquer gsettings gtk-theme=adw-gtk3-dark + color-scheme=prefer-dark
- Réécrire violet-chaton-gtk.css avec les variables exactes de COSMIC dark.css
  (les anciennes règles CSS explicites étaient ignorées par adw-gtk3-dark)
- Corriger deploy_file pour ne pas suivre les symlinks COSMIC (évite
  d'écraser cosmic/dark.css par erreur)
- Bloquer l'exécution en tant que root (causait des erreurs mkdir)
- Renommer CosmicTheme.Light/name en Violet-chaton pour cohérence
2026-02-23 18:56:47 +01:00
91 changed files with 6529 additions and 2818 deletions

View File

@@ -0,0 +1,34 @@
// ── violet-chaton v2 — AGS config ───────────────────────────────────────────
// Barre + OSD + Launcher + Notifications
// Modulaire : Hyprland = full, COSMIC = bar + OSD + launcher + notifs
import { Bar } from "./widgets/Bar.js";
import { OSD } from "./widgets/OSD.js";
import { Launcher } from "./widgets/Launcher.js";
import { Notifications } from "./widgets/Notifications.js";
// ── Compositor detection ────────────────────────────────────────────────────
const compositor = (() => {
const session = Utils.exec("echo $XDG_CURRENT_DESKTOP").trim();
if (session.includes("Hyprland")) return "hyprland";
if (session.includes("COSMIC")) return "cosmic";
return "unknown";
})();
print(`[violet-chaton] compositor: ${compositor}`);
// ── Windows ─────────────────────────────────────────────────────────────────
const windows = (monitor) => {
const wins = [
Bar(monitor, compositor),
OSD(monitor),
Launcher(monitor),
Notifications(monitor),
];
return wins;
};
export default {
style: `${App.configDir}/css/style.css`,
windows: windows(0).flat(),
};

View File

@@ -0,0 +1,385 @@
/* ── violet-chaton v2 — AGS stylesheet ────────────────────────────────────────
*
* Palette :
* crust #1a0e27
* base #261537
* mantle #341c4a
* surface0 #3d2454
* surface1 #493161
* surface2 #5a3875
* magenta #ff4da6 accent primaire
* lilac #c9a0ff accent secondaire
* mitsuri #9adba8 vert pastel
* lavande #a4b4ff bleu-violet
* champagne #e8c87a or chaud
* danger #f25c7a
* text #f0eaf8
* subtext1 #c4b8d4
* subtext0 #9a8fad
* muted #716686
*
* ─────────────────────────────────────────────────────────────────────────── */
/* ── Reset ───────────────────────────────────────────────────────────────── */
* {
font-family: "Maple Mono NF", "MapleMono Nerd Font", monospace;
font-size: 13px;
font-weight: bold;
}
/* ══════════════════════════════════════════════════════════════════════════
* BAR — 3 pills glassmorphism (island floating)
* ══════════════════════════════════════════════════════════════════════════ */
.bar {
background: transparent;
}
.bar .modules-left,
.bar .modules-center,
.bar .modules-right {
background: alpha(#261537, 0.88);
border-radius: 14px;
border: 3px solid alpha(#ff4da6, 0.60);
margin: 8px 4px;
padding: 0 4px;
}
.bar .modules-left:hover,
.bar .modules-center:hover,
.bar .modules-right:hover {
border-color: #ff4da6;
box-shadow: 0 4px 28px alpha(#c9a0ff, 0.18);
}
/* ── Launcher button ─────────────────────────────────────────────────────── */
.bar .launcher-btn {
color: #ff4da6;
font-size: 19px;
padding: 0 14px 0 18px;
min-width: 0;
min-height: 0;
}
.bar .launcher-btn:hover {
color: #c9a0ff;
}
/* ── Separator ───────────────────────────────────────────────────────────── */
.bar .separator {
color: alpha(#f0eaf8, 0.12);
font-size: 11px;
padding: 0 4px;
font-weight: normal;
}
/* ── Clock ───────────────────────────────────────────────────────────────── */
.bar .clock {
color: #ff4da6;
font-weight: 900;
font-size: 14px;
letter-spacing: 0.04em;
padding: 0 10px;
}
.bar .clock:hover {
color: #c9a0ff;
}
/* ── Date ────────────────────────────────────────────────────────────────── */
.bar .date {
color: #a4b4ff;
font-size: 12px;
font-weight: normal;
padding: 0 10px 0 2px;
}
/* ── System modules ──────────────────────────────────────────────────────── */
.bar .cpu { color: #a4b4ff; }
.bar .cpu.warning { color: #e8c87a; }
.bar .cpu.critical { color: #f25c7a; }
.bar .ram { color: #ff4da6; }
.bar .ram.warning { color: #e8c87a; }
.bar .ram.critical { color: #f25c7a; }
.bar .temp {
color: alpha(#a4b4ff, 0.60);
font-size: 11px;
font-weight: normal;
}
.bar .temp.warning { color: #e8c87a; }
.bar .temp.critical { color: #f25c7a; }
/* ── Network ─────────────────────────────────────────────────────────────── */
.bar .network {
color: #a4b4ff;
font-size: 11px;
font-weight: normal;
}
.bar .network.disconnected { color: #f25c7a; }
.bar .network.wifi { color: alpha(#a4b4ff, 0.80); }
/* ── Volume ──────────────────────────────────────────────────────────────── */
.bar .volume { color: #ff4da6; }
.bar .volume.muted { color: alpha(#ff4da6, 0.30); }
/* ── Battery ─────────────────────────────────────────────────────────────── */
.bar .battery { color: #ff4da6; }
.bar .battery.charging { color: #9adba8; }
.bar .battery.low { color: #f25c7a; }
.bar .battery.warning { color: #e8c87a; }
/* ── Media (MPRIS) ───────────────────────────────────────────────────────── */
.bar .media {
color: #c9a0ff;
font-size: 12px;
font-weight: normal;
padding: 0 10px;
}
.bar .media.paused {
color: alpha(#c9a0ff, 0.50);
font-style: italic;
}
/* ── Systray ─────────────────────────────────────────────────────────────── */
.bar .systray { padding: 0 8px; }
.bar .systray .passive { opacity: 0.5; }
/* ── Workspaces (Hyprland only) ──────────────────────────────────────────── */
.bar .workspaces button {
background: transparent;
color: #716686;
min-width: 24px;
min-height: 24px;
border-radius: 8px;
margin: 2px;
padding: 0;
}
.bar .workspaces button.active {
background: alpha(#ff4da6, 0.20);
color: #ff4da6;
border: 1px solid alpha(#ff4da6, 0.40);
}
.bar .workspaces button.occupied {
color: #c9a0ff;
}
.bar .workspaces button:hover {
background: alpha(#c9a0ff, 0.12);
color: #c9a0ff;
}
/* ── Power button ────────────────────────────────────────────────────────── */
.bar .power-btn {
color: #f25c7a;
font-size: 15px;
padding: 0 14px 0 8px;
min-width: 0;
min-height: 0;
}
.bar .power-btn:hover { color: #ff4da6; }
/* ── Hover global modules ────────────────────────────────────────────────── */
.bar .cpu:hover,
.bar .ram:hover,
.bar .temp:hover,
.bar .network:hover,
.bar .volume:hover,
.bar .battery:hover {
color: #c9a0ff;
}
/* ══════════════════════════════════════════════════════════════════════════
* OSD — volume / brightness overlay
* ══════════════════════════════════════════════════════════════════════════ */
.osd {
background: alpha(#261537, 0.92);
border-radius: 14px;
border: 2px solid alpha(#ff4da6, 0.40);
padding: 12px 20px;
margin: 0 0 40px 0;
}
.osd .icon {
color: #ff4da6;
font-size: 24px;
margin-right: 12px;
}
.osd progressbar trough {
background: #3d2454;
border-radius: 8px;
min-height: 8px;
min-width: 200px;
}
.osd progressbar progress {
border-radius: 8px;
min-height: 8px;
background: linear-gradient(to right, #ff4da6, #c9a0ff, #a4b4ff, #9adba8);
}
.osd .label {
color: #f0eaf8;
font-size: 12px;
margin-left: 8px;
}
/* ══════════════════════════════════════════════════════════════════════════
* LAUNCHER — app search
* ══════════════════════════════════════════════════════════════════════════ */
.launcher {
background: alpha(#1a0e27, 0.94);
border-radius: 14px;
border: 2px solid alpha(#ff4da6, 0.38);
padding: 10px;
min-width: 500px;
}
.launcher .search {
background: alpha(#261537, 0.75);
border-radius: 12px;
border: 1px solid alpha(#5a3875, 0.50);
padding: 9px 14px;
color: #f0eaf8;
caret-color: #ff4da6;
font-size: 14px;
}
.launcher .search:focus {
border-color: alpha(#ff4da6, 0.60);
}
.launcher .app-item {
background: transparent;
border-radius: 8px;
padding: 7px 10px;
color: #f0eaf8;
}
.launcher .app-item:hover,
.launcher .app-item:focus {
background: alpha(#ff4da6, 0.16);
border: 1px solid alpha(#ff4da6, 0.32);
}
.launcher .app-item:hover label,
.launcher .app-item:focus label {
color: #ff4da6;
}
.launcher .app-item image {
margin-right: 10px;
}
.launcher .no-results {
color: #716686;
padding: 20px;
}
/* ══════════════════════════════════════════════════════════════════════════
* NOTIFICATIONS
* ══════════════════════════════════════════════════════════════════════════ */
.notification {
background: alpha(#261537, 0.94);
border-radius: 14px;
border: 2px solid alpha(#c9a0ff, 0.30);
padding: 12px;
margin: 8px;
min-width: 350px;
}
.notification .title {
color: #ff4da6;
font-weight: bold;
font-size: 13px;
}
.notification .body {
color: #c4b8d4;
font-weight: normal;
font-size: 12px;
}
.notification .app-name {
color: #716686;
font-size: 11px;
}
.notification .time {
color: #716686;
font-size: 10px;
}
.notification .close-btn {
color: #716686;
font-size: 14px;
min-width: 0;
min-height: 0;
padding: 2px 6px;
border-radius: 6px;
}
.notification .close-btn:hover {
color: #f25c7a;
background: alpha(#f25c7a, 0.12);
}
.notification .actions button {
background: alpha(#5a3875, 0.50);
color: #c9a0ff;
border-radius: 8px;
padding: 4px 12px;
margin: 4px 4px 0 0;
}
.notification .actions button:hover {
background: alpha(#ff4da6, 0.20);
color: #ff4da6;
}
/* ── Urgency levels ──────────────────────────────────────────────────────── */
.notification.critical {
border-color: alpha(#f25c7a, 0.60);
}
.notification.critical .title {
color: #f25c7a;
}
/* ══════════════════════════════════════════════════════════════════════════
* TOOLTIP — shared
* ══════════════════════════════════════════════════════════════════════════ */
tooltip {
background: alpha(#1a0e27, 0.96);
border: 1px solid alpha(#ff4da6, 0.30);
border-radius: 10px;
color: #f0eaf8;
padding: 6px 10px;
}

View File

@@ -0,0 +1,228 @@
// ── violet-chaton v2 — Bar widget ───────────────────────────────────────────
// 3 pills glassmorphism — modulaire selon compositor
const audio = await Service.import("audio");
const battery = await Service.import("battery");
const network = await Service.import("network");
const systemtray = await Service.import("systemtray");
const mpris = await Service.import("mpris");
// ── Helpers ─────────────────────────────────────────────────────────────────
const Separator = () => Widget.Label({ className: "separator", label: "│" });
const Clock = () => Widget.Label({
className: "clock",
connections: [[1000, (self) => {
self.label = Utils.exec("date +%H:%M");
}]],
});
const DateWidget = () => Widget.Label({
className: "date",
connections: [[60000, (self) => {
self.label = Utils.exec("date '+%a %d %b'");
}]],
});
// ── System ──────────────────────────────────────────────────────────────────
const CPU = () => Widget.Label({
className: "cpu",
connections: [[2000, (self) => {
const usage = Math.round(
Number(Utils.exec(`bash -c "top -bn1 | awk '/^%Cpu/ {print 100-$8}'"`)
));
self.label = `󰻠 ${usage}%`;
self.toggleClassName("warning", usage > 70);
self.toggleClassName("critical", usage > 90);
}]],
});
const RAM = () => Widget.Label({
className: "ram",
connections: [[2000, (self) => {
const used = Utils.exec(`bash -c "free -m | awk '/^Mem:/ {printf \"%.1f\", $3/1024}'"`)
const total = Utils.exec(`bash -c "free -m | awk '/^Mem:/ {printf \"%.1f\", $2/1024}'"`)
const pct = Math.round((parseFloat(used) / parseFloat(total)) * 100);
self.label = `󰑭 ${used}G`;
self.toggleClassName("warning", pct > 70);
self.toggleClassName("critical", pct > 90);
}]],
});
// ── Network ─────────────────────────────────────────────────────────────────
const Network = () => Widget.Label({
className: "network",
connections: [[network, (self) => {
if (network.primary === "wifi") {
const wifi = network.wifi;
self.label = `󰤨 ${wifi?.ssid || ""}`;
self.toggleClassName("wifi", true);
self.toggleClassName("disconnected", false);
} else if (network.primary === "wired") {
self.label = "󰈀 Eth";
self.toggleClassName("wifi", false);
self.toggleClassName("disconnected", false);
} else {
self.label = "󰤮 ";
self.toggleClassName("disconnected", true);
}
}]],
});
// ── Volume ──────────────────────────────────────────────────────────────────
const Volume = () => Widget.Button({
className: "volume",
onClicked: () => { audio.speaker.isMuted = !audio.speaker.isMuted; },
child: Widget.Label({
connections: [[audio, (self) => {
const vol = Math.round((audio.speaker?.volume || 0) * 100);
const muted = audio.speaker?.isMuted;
const icon = muted ? "󰝟" : vol > 66 ? "󰕾" : vol > 33 ? "󰖀" : "󰕿";
self.label = `${icon} ${vol}%`;
self.parent?.toggleClassName("muted", muted);
}, "speaker-changed"]],
}),
});
// ── Battery ─────────────────────────────────────────────────────────────────
const Battery = () => Widget.Label({
className: "battery",
visible: battery.bind("available"),
connections: [[battery, (self) => {
const pct = battery.percent;
const charging = battery.charging;
const icon = charging ? "󰂄" : pct > 80 ? "󰁹" : pct > 60 ? "󰂀" :
pct > 40 ? "󰁾" : pct > 20 ? "󰁻" : "󰂎";
self.label = `${icon} ${pct}%`;
self.toggleClassName("charging", charging);
self.toggleClassName("low", pct <= 20 && !charging);
self.toggleClassName("warning", pct <= 30 && !charging);
}]],
});
// ── Media ───────────────────────────────────────────────────────────────────
const Media = () => Widget.Label({
className: "media",
connections: [[mpris, (self) => {
const player = mpris.players[0];
if (!player) {
self.visible = false;
return;
}
self.visible = true;
const artist = player.trackArtists?.join(", ") || "";
const title = player.trackTitle || "";
const icon = player.playBackStatus === "Playing" ? " " : " ";
self.label = `${icon}${artist ? artist + " — " : ""}${title}`.slice(0, 50);
self.toggleClassName("paused", player.playBackStatus !== "Playing");
}]],
});
// ── Systray ─────────────────────────────────────────────────────────────────
const SysTray = () => Widget.Box({
className: "systray",
children: systemtray.bind("items").transform((items) =>
items.map((item) =>
Widget.Button({
child: Widget.Icon({ icon: item.bind("icon"), size: 16 }),
tooltipMarkup: item.bind("tooltip-markup"),
onPrimaryClick: (_, event) => item.activate(event),
onSecondaryClick: (_, event) => item.openMenu(event),
})
)
),
});
// ── Launcher button ─────────────────────────────────────────────────────────
const LauncherBtn = () => Widget.Button({
className: "launcher-btn",
label: "󱄅",
onClicked: () => App.toggleWindow("launcher"),
});
// ── Power button ────────────────────────────────────────────────────────────
const PowerBtn = () => Widget.Button({
className: "power-btn",
label: "⏻",
onClicked: () => Utils.exec("bash -c 'systemctl poweroff'"),
});
// ── Workspaces (Hyprland only) ──────────────────────────────────────────────
const Workspaces = (compositor) => {
if (compositor !== "hyprland") return Widget.Box({});
const hyprland = await Service.import("hyprland");
return Widget.Box({
className: "workspaces",
children: hyprland.bind("workspaces").transform((ws) =>
ws.sort((a, b) => a.id - b.id)
.filter((w) => w.id > 0)
.map((w) =>
Widget.Button({
onClicked: () => hyprland.messageAsync(`dispatch workspace ${w.id}`),
child: Widget.Label({ label: `${w.id}` }),
className: hyprland.active.workspace.bind("id").transform(
(id) => id === w.id ? "active" : w.windows > 0 ? "occupied" : ""
),
})
)
),
});
};
// ── Bar assembly ────────────────────────────────────────────────────────────
export const Bar = (monitor, compositor) => Widget.Window({
name: `bar-${monitor}`,
monitor,
anchor: ["top", "left", "right"],
exclusivity: "exclusive",
className: "bar",
child: Widget.CenterBox({
startWidget: Widget.Box({
className: "modules-left",
children: [
LauncherBtn(),
Separator(),
Workspaces(compositor),
Separator(),
CPU(),
RAM(),
],
}),
centerWidget: Widget.Box({
className: "modules-center",
children: [
Media(),
],
}),
endWidget: Widget.Box({
className: "modules-right",
hpack: "end",
children: [
Network(),
Separator(),
Volume(),
Separator(),
Battery(),
Separator(),
DateWidget(),
Clock(),
Separator(),
SysTray(),
Separator(),
PowerBtn(),
],
}),
}),
});

View File

@@ -0,0 +1,99 @@
// ── violet-chaton v2 — Launcher widget ──────────────────────────────────────
// App launcher avec recherche fuzzy
const applications = await Service.import("applications");
const AppItem = (app) => Widget.Button({
className: "app-item",
onClicked: () => {
app.launch();
App.closeWindow("launcher");
},
child: Widget.Box({
children: [
Widget.Icon({ icon: app.iconName || "application-x-executable", size: 24 }),
Widget.Box({
vertical: true,
children: [
Widget.Label({
label: app.name,
xalign: 0,
truncate: "end",
}),
Widget.Label({
label: app.description || "",
xalign: 0,
truncate: "end",
className: "description",
css: "color: #716686; font-size: 11px; font-weight: normal;",
}),
],
}),
],
}),
});
export const Launcher = (monitor) => {
let apps = applications.list;
const list = Widget.Box({
vertical: true,
spacing: 2,
});
const entry = Widget.Entry({
className: "search",
placeholderText: "Rechercher...",
onAccept: () => {
const first = apps[0];
if (first) {
first.launch();
App.closeWindow("launcher");
}
},
onChange: ({ text }) => {
apps = applications.query(text || "");
list.children = apps.slice(0, 12).map(AppItem);
if (apps.length === 0) {
list.children = [Widget.Label({
className: "no-results",
label: "Aucun resultat",
})];
}
},
});
return Widget.Window({
name: "launcher",
monitor,
anchor: ["top"],
layer: "overlay",
visible: false,
keymode: "exclusive",
setup: (self) => {
self.keybind("Escape", () => App.closeWindow("launcher"));
self.hook(App, (_, name, visible) => {
if (name === "launcher" && visible) {
entry.text = "";
apps = applications.list;
list.children = apps.slice(0, 12).map(AppItem);
entry.grab_focus();
}
});
},
child: Widget.Box({
className: "launcher",
vertical: true,
children: [
Widget.Scrollable({
hscroll: "never",
vscroll: "automatic",
css: "min-height: 400px;",
child: list,
}),
entry,
],
}),
});
};

View File

@@ -0,0 +1,105 @@
// ── violet-chaton v2 — Notifications widget ─────────────────────────────────
// Popup notifications stylees — urgency-aware
const notifications = await Service.import("notifications");
// Ne pas déranger
notifications.popupTimeout = 5000;
notifications.cacheActions = true;
const NotificationIcon = (notif) => {
if (notif.image) {
return Widget.Box({
css: `
min-width: 48px; min-height: 48px;
background-image: url("${notif.image}");
background-size: cover;
background-position: center;
border-radius: 8px;
margin-right: 10px;
`,
});
}
return Widget.Icon({
icon: notif.appIcon || notif.appEntry || "dialog-information",
size: 36,
css: "margin-right: 10px;",
});
};
const Notification = (notif) => Widget.Box({
className: `notification ${notif.urgency}`,
vertical: true,
children: [
Widget.Box({
children: [
NotificationIcon(notif),
Widget.Box({
vertical: true,
hexpand: true,
children: [
Widget.Box({
children: [
Widget.Label({
className: "title",
label: notif.summary,
xalign: 0,
hexpand: true,
truncate: "end",
}),
Widget.Label({
className: "time",
label: new Date(notif.time * 1000)
.toLocaleTimeString("fr-FR", {
hour: "2-digit",
minute: "2-digit",
}),
}),
Widget.Button({
className: "close-btn",
label: "✕",
onClicked: () => notif.close(),
}),
],
}),
Widget.Label({
className: "app-name",
label: notif.appName || "",
xalign: 0,
}),
],
}),
],
}),
...(notif.body ? [Widget.Label({
className: "body",
label: notif.body,
xalign: 0,
wrap: true,
useMarkup: true,
})] : []),
...(notif.actions.length > 0 ? [Widget.Box({
className: "actions",
children: notif.actions.map((action) =>
Widget.Button({
label: action.label,
onClicked: () => notif.invoke(action.id),
})
),
})] : []),
],
});
export const Notifications = (monitor) => Widget.Window({
name: `notifications-${monitor}`,
monitor,
anchor: ["top", "right"],
layer: "overlay",
child: Widget.Box({
vertical: true,
css: "min-width: 350px;",
children: notifications.bind("popups").transform((popups) =>
popups.map(Notification)
),
}),
});

View File

@@ -0,0 +1,95 @@
// ── violet-chaton v2 — OSD widget ───────────────────────────────────────────
// Overlay volume / brightness avec gradient Mitsuri
const audio = await Service.import("audio");
// ── OSD reveal timer ────────────────────────────────────────────────────────
let osdTimeout = null;
const showOSD = (window) => {
window.visible = true;
if (osdTimeout) clearTimeout(osdTimeout);
osdTimeout = setTimeout(() => {
window.visible = false;
}, 1500);
};
// ── Volume OSD ──────────────────────────────────────────────────────────────
const VolumeOSD = () => {
const icon = Widget.Label({ className: "icon" });
const progress = Widget.ProgressBar();
const label = Widget.Label({ className: "label" });
const box = Widget.Box({
className: "osd",
children: [icon, progress, label],
connections: [[audio, (self) => {
const vol = audio.speaker?.volume || 0;
const muted = audio.speaker?.isMuted;
icon.label = muted ? "󰝟" : vol > 0.66 ? "󰕾" : vol > 0.33 ? "󰖀" : "󰕿";
progress.value = vol;
label.label = `${Math.round(vol * 100)}%`;
}, "speaker-changed"]],
});
return box;
};
// ── Brightness OSD ──────────────────────────────────────────────────────────
const BrightnessOSD = () => {
const icon = Widget.Label({ className: "icon", label: "󰃞" });
const progress = Widget.ProgressBar();
const label = Widget.Label({ className: "label" });
const getBrightness = () => {
try {
const max = Number(Utils.exec("brightnessctl max"));
const cur = Number(Utils.exec("brightnessctl get"));
return max > 0 ? cur / max : 0;
} catch {
return 0;
}
};
const box = Widget.Box({
className: "osd",
children: [icon, progress, label],
connections: [[500, (self) => {
const val = getBrightness();
progress.value = val;
label.label = `${Math.round(val * 100)}%`;
}]],
});
return box;
};
// ── OSD windows ─────────────────────────────────────────────────────────────
export const OSD = (monitor) => {
const volumeWin = Widget.Window({
name: `osd-volume-${monitor}`,
monitor,
anchor: ["bottom"],
layer: "overlay",
visible: false,
child: VolumeOSD(),
});
const brightnessWin = Widget.Window({
name: `osd-brightness-${monitor}`,
monitor,
anchor: ["bottom"],
layer: "overlay",
visible: false,
child: BrightnessOSD(),
});
// Auto-show on volume change
Utils.merge([audio.speaker?.bind("volume")], () => showOSD(volumeWin));
return [volumeWin, brightnessWin];
};

View File

@@ -1,376 +1,19 @@
## ── violet-chaton config ────────────────────────────────────────────────────── ## ── violet-chaton v2 — atuin config ──────────────────────────────────────────
dialect = "uk" # format de date européen dialect = "uk"
timezone = "local" timezone = "local"
search_mode = "fuzzy" # recherche floue comme fzf search_mode = "fuzzy"
filter_mode = "global" # cherche dans tout l'historique filter_mode = "global"
style = "full" # interface complète style = "full"
inline_height = 40 # occupe 40% du terminal inline_height = 40
invert = true # barre de recherche en haut invert = true
show_preview = true # aperçu de la commande sélectionnée show_preview = true
enter_accept = true # Entrée accepte directement enter_accept = true
[theme] [theme]
name = "violet-chaton" name = "violet-chaton"
## ── fin config violet-chaton ──────────────────────────────────────────────────
## Base directory for Atuin data files (databases, keys, session, etc.)
## All data file paths default to being relative to this directory.
## linux/mac: ~/.local/share/atuin (or XDG_DATA_HOME/atuin)
## windows: %USERPROFILE%/.local/share/atuin
# data_dir = "~/.local/share/atuin"
## where to store your database, default is your system data directory
## linux/mac: ~/.local/share/atuin/history.db
## windows: %USERPROFILE%/.local/share/atuin/history.db
# db_path = "~/.history.db"
## where to store your encryption key, default is your system data directory
## linux/mac: ~/.local/share/atuin/key
## windows: %USERPROFILE%/.local/share/atuin/key
# key_path = "~/.key"
## where to store your auth session token, default is your system data directory
## linux/mac: ~/.local/share/atuin/session
## windows: %USERPROFILE%/.local/share/atuin/session
# session_path = "~/.session"
## date format used, either "us" or "uk"
# dialect = "us"
## default timezone to use when displaying time
## either "l", "local" to use the system's current local timezone, or an offset
## from UTC in the format of "<+|->H[H][:M[M][:S[S]]]"
## for example: "+9", "-05", "+03:30", "-01:23:45", etc.
# timezone = "local"
## enable or disable automatic sync
# auto_sync = true
## enable or disable automatic update checks
# update_check = true
## address of the sync server
# sync_address = "https://api.atuin.sh"
## how often to sync history. note that this is only triggered when a command
## is ran, so sync intervals may well be longer
## set it to 0 to sync after every command
# sync_frequency = "10m"
## which search mode to use
## possible values: prefix, fulltext, fuzzy, skim
# search_mode = "fuzzy"
## which filter mode to use by default
## possible values: "global", "host", "session", "session-preload", "directory", "workspace"
## consider using search.filters to customize the enablement and order of filter modes
# filter_mode = "global"
## With workspace filtering enabled, Atuin will filter for commands executed
## in any directory within a git repository tree (default: false).
##
## To use workspace mode by default when available, set this to true and
## set filter_mode to "workspace" or leave it unspecified and
## set search.filters to include "workspace" before other filter modes.
# workspaces = false
## which filter mode to use when atuin is invoked from a shell up-key binding
## the accepted values are identical to those of "filter_mode"
## leave unspecified to use same mode set in "filter_mode"
# filter_mode_shell_up_key_binding = "global"
## which search mode to use when atuin is invoked from a shell up-key binding
## the accepted values are identical to those of "search_mode"
## leave unspecified to use same mode set in "search_mode"
# search_mode_shell_up_key_binding = "fuzzy"
## which style to use
## possible values: auto, full, compact
# style = "auto"
## the maximum number of lines the interface should take up
## set it to 0 to always go full screen
# inline_height = 0
## the maximum number of lines the interface should take up
## when atuin is invoked from a shell up-key binding
## the accepted values are identical to those of "inline_height"
# inline_height_shell_up_key_binding = 0
## Invert the UI - put the search bar at the top , Default to `false`
# invert = false
## enable or disable showing a preview of the selected command
## useful when the command is longer than the terminal width and is cut off
# show_preview = true
## what to do when the escape key is pressed when searching
## possible values: return-original, return-query
# exit_mode = "return-original"
## possible values: emacs, subl
# word_jump_mode = "emacs"
## characters that count as a part of a word
# word_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
## number of context lines to show when scrolling by pages
# scroll_context_lines = 1
## use ctrl instead of alt as the shortcut modifier key for numerical UI shortcuts
## alt-0 .. alt-9
# ctrl_n_shortcuts = false
## Show numeric shortcuts (1..9) beside list items in the TUI
## set to false to hide the moving numbers if you find them distracting
# show_numeric_shortcuts = true
## default history list format - can also be specified with the --format arg
# history_format = "{time}\t{command}\t{duration}"
## prevent commands matching any of these regexes from being written to history.
## Note that these regular expressions are unanchored, i.e. if they don't start
## with ^ or end with $, they'll match anywhere in the command.
## For details on the supported regular expression syntax, see
## https://docs.rs/regex/latest/regex/#syntax
# history_filter = [
# "^secret-cmd",
# "^innocuous-cmd .*--secret=.+",
# ]
## prevent commands run with cwd matching any of these regexes from being written
## to history. Note that these regular expressions are unanchored, i.e. if they don't
## start with ^ or end with $, they'll match anywhere in CWD.
## For details on the supported regular expression syntax, see
## https://docs.rs/regex/latest/regex/#syntax
# cwd_filter = [
# "^/very/secret/area",
# ]
## Configure the maximum height of the preview to show.
## Useful when you have long scripts in your history that you want to distinguish
## by more than the first few lines.
# max_preview_height = 4
## Configure whether or not to show the help row, which includes the current Atuin
## version (and whether an update is available), a keymap hint, and the total
## amount of commands in your history.
# show_help = true
## Configure whether or not to show tabs for search and inspect
# show_tabs = true
## Configure whether or not the tabs row may be auto-hidden, which includes the current Atuin
## tab, such as Search or Inspector, and other tabs you may wish to see. This will
## only be hidden if there are fewer than this count of lines available, and does not affect the use
## of keyboard shortcuts to switch tab. 0 to never auto-hide, default is 8 (lines).
## This is ignored except in `compact` mode.
# auto_hide_height = 8
## Defaults to true. This matches history against a set of default regex, and will not save it if we get a match. Defaults include
## 1. AWS key id
## 2. Github pat (old and new)
## 3. Slack oauth tokens (bot, user)
## 4. Slack webhooks
## 5. Stripe live/test keys
# secrets_filter = true
## Defaults to true. If enabled, upon hitting enter Atuin will immediately execute the command,
## whereas tab will put the command in the prompt for editing.
## If set to false, both enter and tab will place the command in the prompt for editing.
## This applies for new installs. Old installs will keep the old behaviour unless configured otherwise.
enter_accept = true
## Defaults to false. If enabled, when triggered after &&, || or |, Atuin will complete commands to chain rather than replace the current line.
# command_chaining = false
## Defaults to "emacs". This specifies the keymap on the startup of `atuin
## search`. If this is set to "auto", the startup keymap mode in the Atuin
## search is automatically selected based on the shell's keymap where the
## keybinding is defined. If this is set to "emacs", "vim-insert", or
## "vim-normal", the startup keymap mode in the Atuin search is forced to be
## the specified one.
# keymap_mode = "auto"
## Cursor style in each keymap mode. If specified, the cursor style is changed
## in entering the cursor shape. Available values are "default" and
## "{blink,steady}-{block,underline,bar}".
# keymap_cursor = { emacs = "blink-block", vim_insert = "blink-block", vim_normal = "steady-block" }
# network_connect_timeout = 5
# network_timeout = 5
## Timeout (in seconds) for acquiring a local database connection (sqlite)
# local_timeout = 5
## Set this to true and Atuin will minimize motion in the UI - timers will not update live, etc.
## Alternatively, set env NO_MOTION=true
# prefers_reduced_motion = false
[stats]
## Set commands where we should consider the subcommand for statistics. Eg, kubectl get vs just kubectl
# common_subcommands = [
# "apt",
# "cargo",
# "composer",
# "dnf",
# "docker",
# "dotnet",
# "git",
# "go",
# "ip",
# "jj",
# "kubectl",
# "nix",
# "nmcli",
# "npm",
# "pecl",
# "pnpm",
# "podman",
# "port",
# "systemctl",
# "tmux",
# "yarn",
# ]
## Set commands that should be totally stripped and ignored from stats
# common_prefix = ["sudo"]
## Set commands that will be completely ignored from stats
# ignored_commands = [
# "cd",
# "ls",
# "vi"
# ]
[keys]
# Defaults to true. If disabled, using the up/down key won't exit the TUI when scrolled past the first/last entry.
# scroll_exits = true
# Defaults to true. The left arrow key will exit the TUI when scrolling before the first character
# exit_past_line_start = true
# Defaults to true. The right arrow key performs the same functionality as Tab and copies the selected line to the command line to be modified.
# accept_past_line_end = true
# Defaults to false. The left arrow key performs the same functionality as Tab and copies the selected line to the command line to be modified.
# accept_past_line_start = false
# Defaults to false. The backspace key performs the same functionality as Tab and copies the selected line to the command line to be modified when at the start of the line.
# accept_with_backspace = false
[sync] [sync]
# Enable sync v2 by default
# This ensures that sync v2 is enabled for new installs only
# In a later release it will become the default across the board
records = true records = true
[preview]
## which preview strategy to use to calculate the preview height (respects max_preview_height).
## possible values: auto, static
## auto: length of the selected command.
## static: length of the longest command stored in the history.
## fixed: use max_preview_height as fixed height.
# strategy = "auto"
[daemon]
## Enables using the daemon to sync. Requires the daemon to be running in the background. Start it with `atuin daemon`
# enabled = false
## How often the daemon should sync in seconds
# sync_frequency = 300
## The path to the unix socket used by the daemon (on unix systems)
## linux/mac: ~/.local/share/atuin/atuin.sock
## windows: Not Supported
# socket_path = "~/.local/share/atuin/atuin.sock"
## Use systemd socket activation rather than opening the given path (the path must still be correct for the client)
## linux: false
## mac/windows: Not Supported
# systemd_socket = false
## The port that should be used for TCP on non unix systems
# tcp_port = 8889
# [theme]
## Color theme to use for rendering in the terminal.
## There are some built-in themes, including the base theme ("default"),
## "autumn" and "marine". You can add your own themes to the "./themes" subdirectory of your
## Atuin config (or ATUIN_THEME_DIR, if provided) as TOML files whose keys should be one or
## more of AlertInfo, AlertWarn, AlertError, Annotation, Base, Guidance, Important, and
## the string values as lowercase entries from this list:
## https://ogeon.github.io/docs/palette/master/palette/named/index.html
## If you provide a custom theme file, it should be called "NAME.toml" and the theme below
## should be the stem, i.e. `theme = "NAME"` for your chosen NAME.
# name = "autumn"
## Whether the theme manager should output normal or extra information to help fix themes.
## Boolean, true or false. If unset, left up to the theme manager.
# debug = true
[search]
## The list of enabled filter modes, in order of priority.
## The "workspace" mode is skipped when not in a workspace or workspaces = false.
## Default filter mode can be overridden with the filter_mode setting.
# filters = [ "global", "host", "session", "session-preload", "workspace", "directory" ]
[tmux]
## Enable using atuin with tmux popup (requires tmux >= 3.2)
## When enabled and running inside tmux, Atuin will use a popup window for interactive search.
## Set to false to disable the popup.
## This can also be controlled with the ATUIN_TMUX_POPUP environment variable.
## Note: The tmux popup is currently supported in zsh, bash, and fish shells. This currently doesn't work with iTerm native tmux integration.
# enabled = false
## Width of the tmux popup window
## Can be a percentage, or integer (e.g. "100" means 100 characters wide)
# width = "80%"
## Height of the tmux popup window
## Can be a percentage, or integer (e.g. "100" means 100 lines tall)
# height = "60%"
[ui] [ui]
columns = ["exit", "duration", "time", "command"] columns = ["exit", "duration", "time", "command"]
## Columns to display in the interactive search, from left to right.
## The selection indicator (" > ") is always shown first implicitly.
##
## Each column can be specified as a simple string (uses default width)
## or as an object with type, width, and expand:
## { type = "directory", width = 30, expand = true }
##
## Available column types (with default widths):
## duration (5) - Command execution duration (e.g., "123ms")
## time (8) - Relative time since execution (e.g., "59m ago")
## datetime (16) - Absolute timestamp (e.g., "2025-01-22 14:35")
## directory (20) - Working directory (truncated if too long)
## host (15) - Hostname where command was run
## user (10) - Username
## exit (3) - Exit code (colored by success/failure)
## command (*) - The command itself (expands by default)
##
## The "expand" option (default: true for command, false for others) makes a
## column fill remaining space. Only one column should have expand = true.
##
## Default:
# columns = ["duration", "time", "command"]
##
## Examples:
##
## Minimal - more space for commands:
# columns = ["duration", "command"]
##
## With wider directory column:
# columns = ["duration", { type = "directory", width = 30 }, "command"]
##
## Show host for multi-machine sync users:
# columns = ["duration", "time", "host", "command"]
##
## Show exit codes prominently:
# columns = ["exit", "duration", "command"]
##
## Make directory expand instead of command:
# columns = ["duration", "time", { type = "directory", expand = true }, { type = "command", expand = false }]

View File

@@ -2,7 +2,7 @@
Name=Waybar Name=Waybar
Comment=violet-chaton status bar Comment=violet-chaton status bar
Type=Application Type=Application
Exec=waybar Exec=bash -c "exec 9>/tmp/waybar-start.lock; flock -n 9 || exit 0; exec waybar"
Hidden=false Hidden=false
NoDisplay=false NoDisplay=false
X-GNOME-Autostart-enabled=true X-GNOME-Autostart-enabled=true

View File

@@ -4,58 +4,74 @@
"type": "none" "type": "none"
}, },
"display": { "display": {
"separator": " ", "separator": " ",
"color": { "color": {
"keys": "magenta", "keys": "#ff4da6",
"title": "cyan" "title": "#c9a0ff",
"separator": "#5a3875"
} }
}, },
"modules": [ "modules": [
"title", {
"separator", "type": "title",
"format": "{user-name}@{host-name}",
{"type": "os", "key": "󰣚 Système ", "keyColor": "magenta"}, "keyColor": "#ff4da6"
{"type": "kernel", "key": " Kernel ", "keyColor": "magenta"}, },
{"type": "shell", "key": " Shell ", "keyColor": "magenta"}, {
{"type": "terminal", "key": " Terminal ", "keyColor": "magenta"}, "type": "custom",
{"type": "wm", "key": " Bureau ", "keyColor": "magenta"}, "format": "───────────────────────────────────",
{"type": "packages", "key": "󰏖 Paquets ", "keyColor": "magenta"}, "outputColor": "#3d2454"
},
"break", "break",
{"type": "cpu", "key": "󰻠 CPU ", "keyColor": "magenta"}, {"type": "os", "key": " sys", "keyColor": "#ff4da6"},
{"type": "kernel", "key": " ker", "keyColor": "#c9a0ff"},
{"type": "shell", "key": " sh ", "keyColor": "#a4b4ff"},
{"type": "terminal", "key": " trm", "keyColor": "#9adba8"},
{"type": "wm", "key": " wm ", "keyColor": "#c9a0ff"},
{"type": "packages", "key": " pkg", "keyColor": "#e8c87a"},
"break",
{"type": "cpu", "key": " cpu", "keyColor": "#ff4da6"},
{ {
"type": "gpu", "type": "gpu",
"key": "󰍛 GPU ", "key": " gpu",
"keyColor": "magenta", "keyColor": "#c9a0ff",
"hideTypes": ["Integrated"] "hideTypes": ["Integrated"]
}, },
{ {
"type": "display", "type": "display",
"key": "󰹆 Écran ", "key": " dsp",
"keyColor": "magenta", "keyColor": "#a4b4ff",
"format": "{width}x{height} @ {refresh-rate}Hz" "format": "{width}x{height} @ {refresh-rate}Hz"
}, },
{"type": "memory", "key": "󰑭 Mémoire ", "keyColor": "magenta"}, {"type": "memory", "key": " mem", "keyColor": "#ff4da6"},
{ {
"type": "disk", "type": "disk",
"key": "󰋊 Disque ", "key": " dsk",
"keyColor": "magenta", "keyColor": "#e8c87a",
"folders": "/" "folders": "/"
}, },
{"type": "processes", "key": "󰙲 Processus", "keyColor": "magenta"}, {"type": "uptime", "key": " up ", "keyColor": "#716686"},
{"type": "uptime", "key": "󰅐 Uptime ", "keyColor": "magenta"},
"break", "break",
{ {
"type": "media", "type": "media",
"key": " Musique ", "key": " now",
"keyColor": "magenta", "keyColor": "#ff4da6",
"format": "{artist} — {title}" "format": "{artist} — {title}"
}, },
"break", "break",
{
"type": "custom",
"format": "───────────────────────────────────",
"outputColor": "#3d2454"
},
"colors" "colors"
] ]
} }

View File

@@ -18,38 +18,38 @@
navigate = true navigate = true
hyperlinks = true hyperlinks = true
# Lignes supprimées : fond rose sombre # Lignes supprimees : fond magenta sombre
minus-style = syntax "#3d1a2e" minus-style = syntax "#3a1230"
minus-emph-style = syntax "#5a1a38" minus-emph-style = syntax "#5a1848"
# Lignes ajoutées : fond cyan sombre # Lignes ajoutees : fond mitsuri sombre
plus-style = syntax "#0d2a35" plus-style = syntax "#1a3025"
plus-emph-style = syntax "#0d3a48" plus-emph-style = syntax "#1a4530"
# Numéros de ligne # Numeros de ligne
line-numbers-minus-style = "#ff79c6" line-numbers-minus-style = "#ff4da6"
line-numbers-plus-style = "#8be9fd" line-numbers-plus-style = "#9adba8"
line-numbers-zero-style = "#6c7086" line-numbers-zero-style = "#716686"
line-numbers-left-format = " {nm} │" line-numbers-left-format = " {nm} │"
line-numbers-right-format = " {np} │" line-numbers-right-format = " {np} │"
# En-tête de fichier # En-tete de fichier
file-style = "bold #e79cfe" file-style = "bold #c9a0ff"
file-decoration-style = "#3d2454 box" file-decoration-style = "#3d2454 box"
file-added-label = "[+]" file-added-label = "[+]"
file-removed-label = "[-]" file-removed-label = "[-]"
file-modified-label = "[~]" file-modified-label = "[~]"
file-renamed-label = "[»]" file-renamed-label = "[»]"
# En-tête de hunk # En-tete de hunk
hunk-header-style = "syntax #261537" hunk-header-style = "syntax #261537"
hunk-header-decoration-style = "#6c7086 box" hunk-header-decoration-style = "#716686 box"
hunk-header-file-style = "bold #e79cfe" hunk-header-file-style = "bold #c9a0ff"
hunk-header-line-number-style = "#7f849c" hunk-header-line-number-style = "#9a8fad"
# Commit # Commit
commit-decoration-style = "bold #ff79c6 box" commit-decoration-style = "bold #ff4da6 box"
commit-style = "bold #f8f8f2" commit-style = "bold #f0eaf8"
[merge] [merge]
conflictstyle = diff3 conflictstyle = diff3

182
INSTALL/configs/kitty.conf Normal file
View File

@@ -0,0 +1,182 @@
# ── violet-chaton v2 — kitty terminal ────────────────────────────────────────
# Font : Maple Mono NF — cursive italics + ligatures
# Palette : violet-chaton v2 (source : themes/palette.sh)
# ── Font ─────────────────────────────────────────────────────────────────────
font_family Maple Mono NF
bold_font Maple Mono NF Bold
italic_font Maple Mono NF Italic
bold_italic_font Maple Mono NF Bold Italic
font_size 13.0
# Ligatures et font features (cursive italics pour commentaires)
disable_ligatures never
font_features MapleMono-NF-Regular +cv01 +ss01 +ss02 +ss03 +ss05
font_features MapleMono-NF-Italic +cv01 +ss01 +ss02 +ss03 +ss05
font_features MapleMono-NF-Bold +cv01 +ss01 +ss02 +ss03 +ss05
font_features MapleMono-NF-BoldItalic +cv01 +ss01 +ss02 +ss03 +ss05
# ── Cursor + trail effect ────────────────────────────────────────────────────
cursor #ff4da6
cursor_text_color #1a0e27
cursor_shape beam
cursor_beam_thickness 1.8
cursor_blink_interval 0.4
# Undercurl style — vagues violettes pour les erreurs/warnings
undercurl_style thin-sparse
# Couleurs undercurl (utilisees par les apps qui supportent les colored underlines)
# Les terminaux modernes + neovim/bat utilisent ces couleurs automatiquement
# Trail — trainee lumineuse magenta derriere le curseur
cursor_trail 15
cursor_trail_decay 0.05 0.3
cursor_trail_start_threshold 0
# ── Scrollback ───────────────────────────────────────────────────────────────
scrollback_lines 10000
wheel_scroll_multiplier 3
# ── Mouse ────────────────────────────────────────────────────────────────────
mouse_hide_wait 2.0
url_color #a4b4ff
url_style curly
open_url_with default
detect_urls yes
# ── Bell ─────────────────────────────────────────────────────────────────────
enable_audio_bell no
visual_bell_duration 0.0
# ── Window ───────────────────────────────────────────────────────────────────
window_padding_width 8 12
background_opacity 0.92
dynamic_background_opacity yes
dim_opacity 0.75
# Blur (compositor dependent — works on KWin, Hyprland, COSMIC)
# background_blur 24
confirm_os_window_close 0
initial_window_width 120c
initial_window_height 35c
# ── Tab bar ──────────────────────────────────────────────────────────────────
tab_bar_edge top
tab_bar_style powerline
tab_powerline_style slanted
tab_bar_min_tabs 2
active_tab_foreground #1a0e27
active_tab_background #ff4da6
active_tab_font_style bold
inactive_tab_foreground #c4b8d4
inactive_tab_background #3d2454
inactive_tab_font_style normal
tab_bar_background #1a0e27
# ── Palette violet-chaton v2 ─────────────────────────────────────────────────
# Fond
foreground #f0eaf8
background #261537
selection_foreground #1a0e27
selection_background #c9a0ff
# Noir (fonds)
color0 #261537
color8 #5a3875
# Rouge (danger)
color1 #f25c7a
color9 #ff7a96
# Vert (mitsuri)
color2 #9adba8
color10 #b5e8c0
# Jaune (champagne)
color3 #e8c87a
color11 #f0d99a
# Bleu (lavande)
color4 #a4b4ff
color12 #bcc8ff
# Magenta
color5 #ff4da6
color13 #c9a0ff
# Cyan (lilac — pas de vrai cyan, on reste violet)
color6 #c9a0ff
color14 #dfc0ff
# Blanc (texte)
color7 #c4b8d4
color15 #f0eaf8
# ── Marks ────────────────────────────────────────────────────────────────────
mark1_foreground #1a0e27
mark1_background #ff4da6
mark2_foreground #1a0e27
mark2_background #c9a0ff
mark3_foreground #1a0e27
mark3_background #a4b4ff
# ── Keyboard shortcuts ──────────────────────────────────────────────────────
# Clipboard
map ctrl+v paste_from_clipboard
map ctrl+c copy_or_interrupt
# Tabs
map ctrl+shift+t new_tab_with_cwd
map ctrl+shift+l next_tab
map ctrl+shift+h previous_tab
map ctrl+shift+w close_tab
map ctrl+shift+, set_tab_title
# Splits / Windows — tiling dans le terminal
map ctrl+shift+enter new_window_with_cwd
map ctrl+shift+\ launch --cwd=current --location=vsplit
map ctrl+shift+- launch --cwd=current --location=hsplit
map ctrl+shift+z toggle_layout stack
map ctrl+shift+r start_resizing_window
# Navigation entre splits
map ctrl+shift+left neighboring_window left
map ctrl+shift+right neighboring_window right
map ctrl+shift+up neighboring_window up
map ctrl+shift+down neighboring_window down
# Swap splits
map ctrl+shift+f move_window_forward
map ctrl+shift+b move_window_backward
# Zoom
map ctrl+shift+equal change_font_size all +1.0
map ctrl+shift+minus change_font_size all -1.0
map ctrl+shift+0 change_font_size all 0
# Reload
map ctrl+shift+f5 load_config_file
# Quick layouts
map ctrl+shift+1 goto_layout tall
map ctrl+shift+2 goto_layout fat
map ctrl+shift+3 goto_layout grid
map ctrl+shift+4 goto_layout horizontal
map ctrl+shift+5 goto_layout vertical
# ── Layouts ──────────────────────────────────────────────────────────────────
enabled_layouts tall,fat,grid,horizontal,vertical,stack
# ── Splits styling ──────────────────────────────────────────────────────────
active_border_color #ff4da6
inactive_border_color #3d2454
bell_border_color #f25c7a
window_border_width 1.5pt
window_margin_width 2
single_window_margin_width 0
draw_minimal_borders yes
inactive_text_alpha 0.7

View File

@@ -0,0 +1,37 @@
// violet-chaton v2 — subtle glow shader for kitty
// Adds a soft bloom/glow around bright text characters
// Usage: kitty --override 'background_shader=~/.config/kitty/violet-chaton-glow.glsl'
void main() {
// Sample the original pixel
vec4 color = texture(image, texCoord);
// Only glow on non-background pixels (bright enough)
float brightness = dot(color.rgb, vec3(0.299, 0.587, 0.114));
if (brightness > 0.15) {
// Sample surrounding pixels for bloom
vec2 pixel = 1.0 / textureSize(image, 0);
vec4 glow = vec4(0.0);
float total = 0.0;
for (int x = -2; x <= 2; x++) {
for (int y = -2; y <= 2; y++) {
float weight = 1.0 / (1.0 + float(x*x + y*y));
glow += texture(image, texCoord + vec2(float(x), float(y)) * pixel) * weight;
total += weight;
}
}
glow /= total;
// Blend glow with original — subtle violet tint
float glowBrightness = dot(glow.rgb, vec3(0.299, 0.587, 0.114));
vec3 violetTint = vec3(0.85, 0.55, 1.0); // lilac-ish
vec3 glowColor = glow.rgb * violetTint;
// Mix: 85% original + 15% glow, only where text is
color.rgb = mix(color.rgb, color.rgb + glowColor * 0.12, smoothstep(0.1, 0.4, glowBrightness));
}
gl_FragColor = color;
}

View File

@@ -1,15 +1,15 @@
gui: gui:
theme: theme:
activeBorderColor: ['#ff79c6', 'bold'] # pink — fenêtre active activeBorderColor: ['#ff4da6', 'bold']
inactiveBorderColor: ['#6c7086'] # muted — fenêtre inactive inactiveBorderColor: ['#716686']
optionsTextColor: ['#e79cfe'] # purple optionsTextColor: ['#c9a0ff']
selectedLineBgColor: ['#3d2454'] # bg-high selectedLineBgColor: ['#5a3875']
selectedRangeBgColor: ['#3d2454'] selectedRangeBgColor: ['#5a3875']
cherryPickedCommitBgColor: ['#3d2454'] cherryPickedCommitBgColor: ['#3d2454']
cherryPickedCommitFgColor: ['#8be9fd'] # cyan cherryPickedCommitFgColor: ['#a4b4ff']
unstagedChangesColor: ['#f38ba8'] # danger unstagedChangesColor: ['#f25c7a']
defaultFgColor: ['#f8f8f2'] # text defaultFgColor: ['#f0eaf8']
searchingActiveBorderColor: ['#8be9fd', 'bold'] # cyan searchingActiveBorderColor: ['#a4b4ff', 'bold']
nerdFontsVersion: '3' nerdFontsVersion: '3'
border: rounded border: rounded
git: git:

View File

@@ -2,21 +2,53 @@
command_timeout = 1000 command_timeout = 1000
add_newline = true add_newline = true
# ── Format ──────────────────────────────────────────────────────────────────── # ── violet-chaton v2 — palette reference ─────────────────────────────────────
# Ligne 1 : infos → durée | mémoire | heure # magenta #ff4da6 accent primaire
# Ligne 2 : # lilac #c9a0ff accent secondaire
format = """ # mitsuri #9adba8 vert pastel (success, fresh)
$os $username$hostname$directory$git_branch$git_status$git_state$git_commit\ # lavande #a4b4ff bleu-violet (info, fonctions)
$python$nodejs$rust$golang$java$docker_context$package\ # champagne #e8c87a or chaud (warning, langages)
$fill\ # danger #f25c7a rouge vif
$cmd_duration$memory_usage$time # text #f0eaf8 texte principal
$status$character""" # subtext1 #c4b8d4 texte secondaire
# subtext0 #9a8fad labels
# muted #716686 desactive
# surface2 #5a3875 selection
# ── Système ─────────────────────────────────────────────────────────────────── # ── Format — 3 lignes ───────────────────────────────────────────────────────
# Ligne 1 : separateur
# Ligne 2 : os | dir | git | langages | ... fill ... | duree | batterie | heure
# Ligne 3 : prompt caractere
format = """
[┌──](bold #5a3875) $os$sudo$username$hostname\
$directory$git_branch$git_status$git_state$git_commit\
$python$nodejs$rust$golang$java$docker_context$package$env_var\
$fill\
$cmd_duration$battery$time
[└─](bold #5a3875) $status$character"""
# ── Palette Starship ─────────────────────────────────────────────────────────
palette = "violet-chaton"
[palettes.violet-chaton]
magenta = "#ff4da6"
lilac = "#c9a0ff"
mitsuri = "#9adba8"
lavande = "#a4b4ff"
champagne = "#e8c87a"
danger = "#f25c7a"
text = "#f0eaf8"
subtext1 = "#c4b8d4"
subtext0 = "#9a8fad"
muted = "#716686"
surface2 = "#5a3875"
crust = "#1a0e27"
# ── Systeme ──────────────────────────────────────────────────────────────────
[os] [os]
disabled = false disabled = false
style = "bold #ff79c6" style = "bold magenta"
format = "[$symbol]($style)" format = "[$symbol]($style)"
[os.symbols] [os.symbols]
@@ -43,38 +75,44 @@ Macos = " "
Windows = "󰍲 " Windows = "󰍲 "
Unknown = " " Unknown = " "
[sudo]
disabled = false
style = "bold danger"
symbol = "󰞀 "
format = "[$symbol]($style)"
[username] [username]
style_user = "#9399b2" style_user = "subtext0"
style_root = "bold #f38ba8" style_root = "bold danger"
format = "[$user]($style)" format = "[$user]($style)"
show_always = true show_always = false
[hostname] [hostname]
style = "#7f849c" style = "muted"
format = "[@$hostname]($style) " format = "[@$hostname ]($style)"
ssh_only = false ssh_only = true
ssh_symbol = "󰣀 " ssh_symbol = "󰣀 "
# ── Navigation ─────────────────────────────────────────────────────────────── # ── Navigation ───────────────────────────────────────────────────────────────
[directory] [directory]
style = "bold #e79cfe" style = "bold lilac"
read_only = " 󰌾" read_only = " 󰌾"
read_only_style = "#f38ba8" read_only_style = "danger"
truncate_to_repo = false truncate_to_repo = false
truncation_length = 4 truncation_length = 4
truncation_symbol = "…/" truncation_symbol = "…/"
format = "[ $path]($style)[$read_only]($read_only_style) " format = "[ $path]($style)[$read_only]($read_only_style) "
# ── Git ────────────────────────────────────────────────────────────────────── # ── Git ──────────────────────────────────────────────────────────────────────
[git_branch] [git_branch]
symbol = " " symbol = " "
style = "bold #ff79c6" style = "bold magenta"
format = "[$symbol$branch]($style) " format = "[$symbol$branch]($style) "
[git_status] [git_status]
style = "#8be9fd" style = "mitsuri"
format = "([$all_status$ahead_behind]($style) )" format = "([$all_status$ahead_behind]($style) )"
ahead = "⇡$count" ahead = "⇡$count"
behind = "⇣$count" behind = "⇣$count"
@@ -88,85 +126,98 @@ deleted = "✘$count"
conflicted = "=$count" conflicted = "=$count"
[git_state] [git_state]
style = "#f9e2af" style = "champagne"
format = '\([$state( $progress_current/$progress_total)]($style)\) ' format = '\([$state( $progress_current/$progress_total)]($style)\) '
[git_commit] [git_commit]
style = "#6c7086" style = "muted"
format = '[\($hash$tag\)]($style) ' format = '[\($hash$tag\)]($style) '
tag_symbol = " 󰓼 " tag_symbol = " 󰓼 "
only_detached = true only_detached = true
# ── Langages ───────────────────────────────────────────────────────────────── # ── Langages ─────────────────────────────────────────────────────────────────
[python] [python]
symbol = " " symbol = " "
style = "#f9e2af" style = "champagne"
format = "[$symbol$version]($style) " format = "[$symbol$version]($style) "
[nodejs] [nodejs]
symbol = " " symbol = " "
style = "#a6e3a1" style = "mitsuri"
format = "[$symbol$version]($style) " format = "[$symbol$version]($style) "
[rust] [rust]
symbol = " " symbol = " "
style = "#ff79c6" style = "magenta"
format = "[$symbol$version]($style) " format = "[$symbol$version]($style) "
[golang] [golang]
symbol = " " symbol = " "
style = "#8be9fd" style = "lavande"
format = "[$symbol$version]($style) " format = "[$symbol$version]($style) "
[java] [java]
symbol = " " symbol = " "
style = "#f38ba8" style = "danger"
format = "[$symbol$version]($style) " format = "[$symbol$version]($style) "
[docker_context] [docker_context]
symbol = " " symbol = " "
style = "#8be9fd" style = "lavande"
format = "[$symbol$context]($style) " format = "[$symbol$context]($style) "
only_with_files = true only_with_files = true
[package] [package]
symbol = "󰏗 " symbol = "󰏗 "
style = "#e79cfe" style = "lilac"
format = "[$symbol$version]($style) " format = "[$symbol$version]($style) "
display_private = false display_private = false
# ── Droite (après $fill) ───────────────────────────────────────────────────── # ── Env var custom — brain_name ──────────────────────────────────────────────
[env_var.brain_name]
variable = "brain_name"
style = "italic muted"
symbol = "󰧑 "
format = "[$symbol$env_value]($style) "
# ── Droite (apres $fill) ────────────────────────────────────────────────────
[fill] [fill]
symbol = " " symbol = ""
style = "#3d2454"
[cmd_duration] [cmd_duration]
format = "[󱎫 $duration]($style) " format = "[󱎫 $duration]($style) "
style = "#7f849c" style = "subtext0"
min_time = 2000 min_time = 2000
[memory_usage] [battery]
disabled = false disabled = false
threshold = 0 format = "[$symbol$percentage]($style) "
symbol = "󰑭 "
style = "#9399b2" [[battery.display]]
format = "[$symbol$ram]($style) " threshold = 30
style = "bold danger"
[[battery.display]]
threshold = 70
style = "champagne"
[time] [time]
disabled = false disabled = false
style = "bold #6c7086" style = "bold muted"
format = "[ $time]($style)" format = "[ $time]($style)"
time_format = "%H:%M" time_format = "%H:%M"
# ── Ligne 2 ─────────────────────────────────────────────────────────────────── # ── Ligne 3 — le prompt ─────────────────────────────────────────────────────
[status] [status]
disabled = false disabled = false
style = "#ff79c6" style = "danger"
format = "[$symbol$status ]($style)" format = "[$symbol$status ]($style)"
symbol = "✘ " symbol = "✘ "
[character] [character]
success_symbol = "[](bold #8be9fd)" success_symbol = "[](bold mitsuri)"
error_symbol = "[](bold #ff79c6)" error_symbol = "[](bold magenta)"

View File

@@ -20,10 +20,10 @@
"custom/launcher", "custom/launcher",
"custom/sep", "custom/sep",
"cpu", "cpu",
"temperature", "custom/cpu-temp",
"custom/gpu", "custom/gpu",
"memory", "memory",
"disk", "custom/disks",
"custom/sep", "custom/sep",
"custom/network" "custom/network"
], ],
@@ -77,15 +77,13 @@
"interval": 2 "interval": 2
}, },
// ── Température ───────────────────────────────────────────────────────── // ── Température CPU (auto-détection) ────────────────────────────────────
"temperature": { "custom/cpu-temp": {
"thermal-zone": 9, "exec": "~/.config/waybar/scripts/cpu-temp.sh",
"format": " {temperatureC}°", "return-type": "json",
"format-critical": " {temperatureC}°", "interval": 2,
"critical-threshold": 80, "format": "{}"
"tooltip": false,
"interval": 2
}, },
// ── GPU ───────────────────────────────────────────────────────────────── // ── GPU ─────────────────────────────────────────────────────────────────
@@ -109,12 +107,13 @@
"interval": 2 "interval": 2
}, },
// ── Disque ────────────────────────────────────────────────────────────── // ── Disques (auto-détection) ─────────────────────────────────────────────
"disk": { "custom/disks": {
"format": "󰋊 {used}", "exec": "~/.config/waybar/scripts/disks.sh",
"tooltip-format": "󰋊 Disque /\n{used} / {total}\n{percentage_used}% utilisé", "return-type": "json",
"interval": 30 "interval": 30,
"format": "{}"
}, },
// ── Réseau ────────────────────────────────────────────────────────────── // ── Réseau ──────────────────────────────────────────────────────────────

View File

@@ -0,0 +1,43 @@
#!/usr/bin/env bash
# cpu-temp.sh — température CPU auto-détection → JSON waybar
# Priorité 1 : thermal zone x86_pkg_temp (Intel) ou k10temp (AMD)
# Priorité 2 : hwmon coretemp / k10temp / zenpower
# Retourne vide si aucune source trouvée
emit() {
local temp=$1
local cls="normal"
(( temp >= 80 )) && cls="critical"
(( temp >= 65 && temp < 80 )) && cls="warning"
printf '{"text":" %d°","tooltip":"CPU %d°C","class":"%s","percentage":%d}\n' \
"$temp" "$temp" "$cls" "$temp"
exit 0
}
# Priorité 1 — thermal_zone x86_pkg_temp (Intel) / TCPU / k10temp (AMD)
for zone in /sys/class/thermal/thermal_zone*/; do
zone_type=$(cat "${zone}type" 2>/dev/null) || continue
case "$zone_type" in
x86_pkg_temp|k10temp|TCPU|cpu_thermal)
temp_raw=$(cat "${zone}temp" 2>/dev/null) || continue
emit $(( temp_raw / 1000 ))
;;
esac
done
# Priorité 2 — hwmon coretemp (Intel desktop) ou k10temp (AMD)
for hw in /sys/class/hwmon/hwmon*/; do
hw_name=$(cat "${hw}name" 2>/dev/null) || continue
case "$hw_name" in
coretemp|k10temp|zenpower|amd_energy)
for f in "${hw}temp1_input" "${hw}temp2_input"; do
[[ -r "$f" ]] || continue
temp_raw=$(cat "$f" 2>/dev/null) || continue
emit $(( temp_raw / 1000 ))
done
;;
esac
done
# Aucune source — module masqué
printf '{"text":"","class":"unavailable"}\n'

View File

@@ -0,0 +1,58 @@
#!/usr/bin/env bash
# disks.sh — liste les vrais systèmes de fichiers montés → JSON waybar
# Exclut tmpfs, devtmpfs, squashfs (snap), overlay, efi, etc.
TEXT=""
TOOLTIP="󰋊 Disques\n"
while IFS= read -r line; do
fs=$(awk '{print $1}' <<< "$line")
size=$(awk '{print $2}' <<< "$line")
used=$(awk '{print $3}' <<< "$line")
avail=$(awk '{print $4}' <<< "$line")
pct=$(awk '{print $5}' <<< "$line")
mnt=$(awk '{print $6}' <<< "$line")
# Exclure mounts sans intérêt
[[ "$mnt" == /snap/* ]] && continue
[[ "$mnt" == /boot/efi ]] && continue
[[ "$mnt" == /boot ]] && continue
[[ "$mnt" == /recovery ]] && continue
[[ "$mnt" == /run* ]] && continue
[[ "$mnt" == /sys* ]] && continue
[[ "$mnt" == /proc* ]] && continue
[[ "$mnt" == /dev* ]] && continue
# Icône selon le point de montage
case "$mnt" in
/) icon="󰋊" ;;
/home) icon="󱂵" ;;
/data*) icon="󱦡" ;;
/media*) icon="󰆼" ;;
/mnt*) icon="󱛟" ;;
*) icon="󰋊" ;;
esac
# Texte compact : icône + montage court + espace utilisé
label=$(basename "$mnt")
[[ "$mnt" == "/" ]] && label="/"
[[ -n "$TEXT" ]] && TEXT+=" "
TEXT+="${icon} ${label}: ${used}"
TOOLTIP+="${icon} ${mnt}\n Utilisé : ${used} / ${size} (${pct})\n Libre : ${avail}\n"
done < <(df -hP --exclude-type=tmpfs \
--exclude-type=devtmpfs \
--exclude-type=squashfs \
--exclude-type=overlay \
--exclude-type=fuse.portal \
--exclude-type=efivarfs \
2>/dev/null | tail -n +2 | sort -k6)
if [[ -z "$TEXT" ]]; then
printf '{"text":"󰋊 N/A","tooltip":"Aucun disque détecté","class":"unavailable"}\n'
else
# Échapper uniquement les guillemets pour JSON (\n reste tel quel = saut de ligne)
TOOLTIP_JSON=$(printf '%s' "$TOOLTIP" | sed 's/"/\\"/g')
printf '{"text":"%s","tooltip":"%s"}\n' "$TEXT" "$TOOLTIP_JSON"
fi

View File

@@ -3,19 +3,17 @@
STATE_FILE="/tmp/waybar_net_state" STATE_FILE="/tmp/waybar_net_state"
# Détecter l'interface active # Détecter l'interface active via la route par défaut (portable sur tous les PC)
IFACE="" IFACE=$(ip route get 1.1.1.1 2>/dev/null \
for candidate in enp7s0 enp6s0 eth0; do | awk '/dev/{for(i=1;i<=NF;i++) if($i=="dev") print $(i+1)}' \
if [[ -d "/sys/class/net/$candidate" && "$(cat /sys/class/net/$candidate/operstate 2>/dev/null)" == "up" ]]; then | head -1)
IFACE="$candidate"; TYPE="eth"; break
if [[ -n "$IFACE" ]]; then
if [[ -d "/sys/class/net/$IFACE/wireless" || -d "/sys/class/net/$IFACE/phy80211" ]]; then
TYPE="wifi"
else
TYPE="eth"
fi 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 fi
if [[ -z "$IFACE" ]]; then if [[ -z "$IFACE" ]]; then

View File

@@ -40,6 +40,12 @@ fi
# ── Affichage JSON ──────────────────────────────────────────────────────────── # ── Affichage JSON ────────────────────────────────────────────────────────────
# PC fixe ou VM sans gestion de profil → module masqué
if [[ ! -f /sys/firmware/acpi/platform_profile ]]; then
printf '{"text":"","class":"unavailable"}\n'
exit 0
fi
PROFILE=$(cat /sys/firmware/acpi/platform_profile 2>/dev/null || echo "unknown") PROFILE=$(cat /sys/firmware/acpi/platform_profile 2>/dev/null || echo "unknown")
case "$PROFILE" in case "$PROFILE" in

View File

@@ -1,245 +0,0 @@
#!/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

@@ -3,9 +3,16 @@
# Lancé depuis le clic sur wireplumber OU backlight # Lancé depuis le clic sur wireplumber OU backlight
import gi import gi
import math
import threading
import urllib.request
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
gi.require_version('GtkLayerShell', '0.1') gi.require_version('GtkLayerShell', '0.1')
from gi.repository import Gtk, Gdk, GtkLayerShell, GLib gi.require_version('GdkPixbuf', '2.0')
gi.require_version('Pango', '1.0')
gi.require_version('PangoCairo', '1.0')
from gi.repository import Gtk, Gdk, GtkLayerShell, GLib, GdkPixbuf, Pango, PangoCairo
import subprocess import subprocess
import os import os
import re import re
@@ -135,6 +142,31 @@ scale.audio.muted slider {
border-color: rgba(108, 112, 134, 0.60); border-color: rgba(108, 112, 134, 0.60);
} }
/* ── Sélecteur de périphérique de sortie ────────────────────────────────────── */
#device-btn {
background-color: transparent;
color: rgba(248, 248, 242, 0.65);
font-family: "JetBrainsMono Nerd Font";
font-size: 11px;
border: 1px solid transparent;
border-radius: 8px;
padding: 5px 10px;
min-width: 0;
}
#device-btn:hover {
background-color: rgba(91, 70, 113, 0.55);
color: #f8f8f2;
border-color: rgba(92, 73, 108, 0.60);
}
#device-btn.active {
background-color: rgba(255, 121, 198, 0.14);
color: #ff79c6;
border-color: rgba(255, 121, 198, 0.55);
}
/* ── Slider luminosité (cyan) ───────────────────────────────────────────────── */ /* ── Slider luminosité (cyan) ───────────────────────────────────────────────── */
scale.bright { scale.bright {
@@ -176,14 +208,55 @@ scale.bright slider:hover {
font-size: 17px; font-size: 17px;
min-width: 28px; min-width: 28px;
} }
/* ── Section MPRIS ────────────────────────────────────────────────────────── */
#mpris-title {
color: #f8f8f2;
font-family: "JetBrainsMono Nerd Font";
font-size: 12px;
font-weight: bold;
}
#mpris-artist {
color: rgba(248, 248, 242, 0.55);
font-family: "JetBrainsMono Nerd Font";
font-size: 11px;
}
#mpris-btn {
background-color: transparent;
color: #e79cfe;
font-family: "JetBrainsMono Nerd Font";
font-size: 16px;
border: none;
border-radius: 6px;
padding: 4px 8px;
min-width: 0;
}
#mpris-btn:hover {
background-color: rgba(231, 156, 254, 0.15);
}
#mpris-btn.play {
font-size: 20px;
color: #ff79c6;
padding: 4px 12px;
}
#mpris-btn.play:hover {
background-color: rgba(255, 121, 198, 0.15);
}
""" """
POPUP_WIDTH = 310 POPUP_WIDTH = 310
# ── Helpers ─────────────────────────────────────────────────────────────────── # ── Helpers ───────────────────────────────────────────────────────────────────
def run(cmd, **kw): def run(cmd, env=None, **kw):
return subprocess.run(cmd, capture_output=True, text=True, timeout=2, **kw) return subprocess.run(cmd, capture_output=True, text=True, timeout=2,
env=env, **kw)
def get_sink_volume(): def get_sink_volume():
r = run(['wpctl', 'get-volume', '@DEFAULT_AUDIO_SINK@']) r = run(['wpctl', 'get-volume', '@DEFAULT_AUDIO_SINK@'])
@@ -239,6 +312,93 @@ def _wob(msg):
except OSError: except OSError:
pass pass
def get_sinks():
"""Retourne [(sink_name, description, is_default)] — exclut SUSPENDED."""
env = {**os.environ, 'LANG': 'C', 'LC_ALL': 'C'}
r_default = run(['pactl', 'get-default-sink'], env=env)
default_name = r_default.stdout.strip()
r_full = run(['pactl', 'list', 'sinks'], env=env)
sinks, state, name, desc = [], None, None, None
for line in r_full.stdout.splitlines():
st = re.search(r'^\s+State:\s+(\S+)', line)
nm = re.search(r'^\s+Name:\s+(.+)$', line)
ds = re.search(r'^\s+Description:\s+(.+)$', line)
if st: state = st.group(1)
elif nm: name = nm.group(1).strip()
elif ds and name:
desc = ds.group(1).strip()
if state != 'SUSPENDED':
sinks.append((name, desc, name == default_name))
state, name, desc = None, None, None
return sinks
def set_default_sink(name):
run(['pactl', 'set-default-sink', name])
def get_sources():
"""Retourne [(source_name, description, is_default)] — exclut les .monitor."""
env = {**os.environ, 'LANG': 'C', 'LC_ALL': 'C'}
r_default = run(['pactl', 'get-default-source'], env=env)
default_name = r_default.stdout.strip()
r_full = run(['pactl', 'list', 'sources'], env=env)
sources, name, desc = [], None, None
for line in r_full.stdout.splitlines():
nm = re.search(r'^\s+Name:\s+(.+)$', line)
ds = re.search(r'^\s+Description:\s+(.+)$', line)
if nm:
name = nm.group(1).strip()
elif ds and name:
desc = ds.group(1).strip()
if '.monitor' not in name:
sources.append((name, desc, name == default_name))
name, desc = None, None
return sources
def set_default_source(name):
run(['pactl', 'set-default-source', name])
def get_mpris_info():
"""Retourne dict ou None si pas de lecteur actif."""
try:
r = run(['playerctl', 'metadata', '--format',
'{{title}}||{{artist}}||{{mpris:artUrl}}||{{status}}'])
except (FileNotFoundError, subprocess.TimeoutExpired):
return None
if r.returncode != 0 or not r.stdout.strip():
return None
parts = r.stdout.strip().split('||', 3)
if len(parts) < 4:
return None
title, artist, art_url, status = [p.strip() for p in parts]
if not title:
return None
return {
'title': title,
'artist': artist,
'art_url': art_url,
'status': status.lower(), # 'playing' | 'paused' | 'stopped'
}
def source_icon(name, desc):
s = (name + desc).lower()
if 'bluetooth' in s or 'bluez' in s: return '󰥰'
if 'usb' in s: return '󱡬'
if 'headset' in s or 'headphone' in s: return '󰋎'
return '󰍬'
def sink_icon(name, desc):
s = (name + desc).lower()
if 'hdmi' in s or 'dp-' in s or 'displayport' in s: return '󰡁'
if 'bluetooth' in s or 'bluez' in s: return '󰥰'
if 'usb' in s: return '󱡬'
if 'headphone' in s or 'headset' in s: return '󰋋'
return '󰓃'
def short_desc(desc, maxlen=16):
return desc if len(desc) <= maxlen else desc[:maxlen - 1] + ''
def vol_icon(muted): def vol_icon(muted):
return '󰖁' if muted else '󰕾' return '󰖁' if muted else '󰕾'
@@ -250,26 +410,109 @@ def bright_icon(pct):
if pct < 67: return '󰃟' if pct < 67: return '󰃟'
return '󰃠' return '󰃠'
# ── Art widget (album art / miniature YouTube) ────────────────────────────────
class ArtWidget(Gtk.DrawingArea):
SIZE = 72
def __init__(self):
super().__init__()
self._pixbuf = None
self._url = None
self.set_size_request(self.SIZE, self.SIZE)
self.connect('draw', self._on_draw)
def load_url(self, url):
if url == self._url:
return
self._url = url
self._pixbuf = None
self.queue_draw()
if not url:
return
threading.Thread(target=self._fetch, args=(url,), daemon=True).start()
def _fetch(self, url):
try:
if url.startswith('file://'):
raw = GdkPixbuf.Pixbuf.new_from_file(url[7:])
else:
req = urllib.request.Request(
url, headers={'User-Agent': 'Mozilla/5.0'})
with urllib.request.urlopen(req, timeout=5) as resp:
data = resp.read()
loader = GdkPixbuf.PixbufLoader()
loader.write(data)
loader.close()
raw = loader.get_pixbuf()
if raw:
s = self.SIZE
ow, oh = raw.get_width(), raw.get_height()
scale = min(s / ow, s / oh)
nw = max(1, int(ow * scale))
nh = max(1, int(oh * scale))
pixbuf = raw.scale_simple(nw, nh, GdkPixbuf.InterpType.BILINEAR)
else:
pixbuf = None
except Exception:
pixbuf = None
GLib.idle_add(self._set_pixbuf, pixbuf, url)
def _set_pixbuf(self, pixbuf, url):
if url == self._url:
self._pixbuf = pixbuf
self.queue_draw()
return False
def _on_draw(self, _widget, cr):
s, r = self.SIZE, 10
# Coins arrondis (clip)
cr.new_sub_path()
cr.arc(r, r, r, math.pi, 3 * math.pi / 2)
cr.arc(s - r, r, r, -math.pi / 2, 0)
cr.arc(s - r, s - r, r, 0, math.pi / 2)
cr.arc(r, s - r, r, math.pi / 2, math.pi)
cr.close_path()
cr.clip()
# Fond violet
cr.set_source_rgba(73/255, 49/255, 97/255, 0.85)
cr.paint()
if self._pixbuf:
pw = self._pixbuf.get_width()
ph = self._pixbuf.get_height()
Gdk.cairo_set_source_pixbuf(cr, self._pixbuf,
(s - pw) / 2, (s - ph) / 2)
cr.paint()
else:
# Icône note de musique (placeholder)
layout = PangoCairo.create_layout(cr)
layout.set_markup('<span font="JetBrainsMono Nerd Font 24">󰝚</span>')
lw, lh = layout.get_size()
cr.set_source_rgba(231/255, 156/255, 254/255, 0.45)
cr.move_to((s - lw / Pango.SCALE) / 2,
(s - lh / Pango.SCALE) / 2)
PangoCairo.show_layout(cr, layout)
# ── Popup ───────────────────────────────────────────────────────────────────── # ── Popup ─────────────────────────────────────────────────────────────────────
class MediaPopup(Gtk.Window): class MediaPopup(Gtk.Window):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self._blk = False self._blk = False
self._has_mpris = 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)
# ── Position — ancré à droite, toujours dans l'écran ─────────────────
GtkLayerShell.init_for_window(self) GtkLayerShell.init_for_window(self)
GtkLayerShell.set_layer(self, GtkLayerShell.Layer.OVERLAY) GtkLayerShell.set_layer(self, GtkLayerShell.Layer.OVERLAY)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.TOP, True) GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.TOP, True)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.LEFT, True) GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.RIGHT, True)
GtkLayerShell.set_margin(self, GtkLayerShell.Edge.TOP, 66) GtkLayerShell.set_margin(self, GtkLayerShell.Edge.TOP, 66)
GtkLayerShell.set_margin(self, GtkLayerShell.Edge.LEFT, margin_left) GtkLayerShell.set_margin(self, GtkLayerShell.Edge.RIGHT, 12)
GtkLayerShell.set_keyboard_mode(self, GtkLayerShell.KeyboardMode.ON_DEMAND) GtkLayerShell.set_keyboard_mode(self, GtkLayerShell.KeyboardMode.ON_DEMAND)
GtkLayerShell.set_exclusive_zone(self, -1) GtkLayerShell.set_exclusive_zone(self, -1)
self.set_decorated(False) self.set_decorated(False)
@@ -295,10 +538,22 @@ class MediaPopup(Gtk.Window):
box.set_name('container') box.set_name('container')
self.add(box) self.add(box)
# ╔═══ LECTURE (MPRIS) — affiché seulement si lecteur actif ════════════╗
mpris_info = get_mpris_info()
if mpris_info:
self._build_mpris_section(box, mpris_info)
sep0 = Gtk.Label(label='' * 34)
sep0.set_name('separator')
box.pack_start(sep0, False, False, 0)
# ╔═══ SORTIE ══════════════════════════════════════════════════════════╗ # ╔═══ SORTIE ══════════════════════════════════════════════════════════╗
sinks = get_sinks()
box.pack_start(self._section_header('SORTIE', '󰕾'), False, False, 0) box.pack_start(self._section_header('SORTIE', '󰕾'), False, False, 0)
box.pack_start(self._device_label( self.sink_device_lbl = self._device_label(
get_node_name('@DEFAULT_AUDIO_SINK@')), False, False, 2) get_node_name('@DEFAULT_AUDIO_SINK@'))
box.pack_start(self.sink_device_lbl, False, False, 2)
if len(sinks) > 1:
box.pack_start(self._sink_selector(sinks), False, False, 4)
sink_row, self.sink_scale, self.sink_pct, self.sink_icon = \ sink_row, self.sink_scale, self.sink_pct, self.sink_icon = \
self._slider_row(sink_vol, sink_muted, 'audio', vol_icon(sink_muted), self._slider_row(sink_vol, sink_muted, 'audio', vol_icon(sink_muted),
self._toggle_sink_mute, '@DEFAULT_AUDIO_SINK@') self._toggle_sink_mute, '@DEFAULT_AUDIO_SINK@')
@@ -309,9 +564,13 @@ class MediaPopup(Gtk.Window):
sep1.set_name('separator') sep1.set_name('separator')
box.pack_start(sep1, False, False, 0) box.pack_start(sep1, False, False, 0)
sources = get_sources()
box.pack_start(self._section_header('ENTRÉE', '󰍬'), False, False, 0) box.pack_start(self._section_header('ENTRÉE', '󰍬'), False, False, 0)
box.pack_start(self._device_label( self.src_device_lbl = self._device_label(
get_node_name('@DEFAULT_AUDIO_SOURCE@')), False, False, 2) get_node_name('@DEFAULT_AUDIO_SOURCE@'))
box.pack_start(self.src_device_lbl, False, False, 2)
if len(sources) > 1:
box.pack_start(self._source_selector(sources), False, False, 4)
src_row, self.src_scale, self.src_pct, self.src_icon = \ src_row, self.src_scale, self.src_pct, self.src_icon = \
self._slider_row(src_vol, src_muted, 'audio', mic_icon(src_muted), self._slider_row(src_vol, src_muted, 'audio', mic_icon(src_muted),
self._toggle_src_mute, '@DEFAULT_AUDIO_SOURCE@') self._toggle_src_mute, '@DEFAULT_AUDIO_SOURCE@')
@@ -333,23 +592,167 @@ class MediaPopup(Gtk.Window):
self.connect('focus-out-event', lambda *_: self.destroy()) self.connect('focus-out-event', lambda *_: self.destroy())
self.show_all() self.show_all()
# GTK3 bug : set_value() avant réalisation → highlight width=0 à max # 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) GLib.idle_add(self._redraw_scales)
self.grab_focus() self.grab_focus()
def _redraw_scales(self): # ── MPRIS section builder ─────────────────────────────────────────────────
"""Force GTK3 à recalculer les highlights.
set_value(même_valeur) est un no-op — on oscille ±1 pour déclencher def _build_mpris_section(self, box, info):
un vrai recalcul de la position du highlight dans le trough.""" box.pack_start(self._section_header('LECTURE', '󰝚'), False, False, 0)
content = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
content.set_margin_top(4)
content.set_margin_bottom(4)
# Album art
self.mpris_art = ArtWidget()
content.pack_start(self.mpris_art, False, False, 0)
# Infos + contrôles
info_col = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
info_col.set_valign(Gtk.Align.CENTER)
self.mpris_title = Gtk.Label(label=info['title'])
self.mpris_title.set_name('mpris-title')
self.mpris_title.set_halign(Gtk.Align.START)
self.mpris_title.set_ellipsize(3)
self.mpris_title.set_max_width_chars(20)
info_col.pack_start(self.mpris_title, False, False, 0)
self.mpris_artist = Gtk.Label(label=info['artist'] or '')
self.mpris_artist.set_name('mpris-artist')
self.mpris_artist.set_halign(Gtk.Align.START)
self.mpris_artist.set_ellipsize(3)
self.mpris_artist.set_max_width_chars(20)
info_col.pack_start(self.mpris_artist, False, False, 0)
# Contrôles prev / play-pause / next
ctrl = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
ctrl.set_margin_top(4)
btn_prev = Gtk.Button(label='󰒮')
btn_prev.set_name('mpris-btn')
btn_prev.connect('clicked', lambda _: run(['playerctl', 'previous']))
self.btn_play = Gtk.Button(
label='󰏤' if info['status'] == 'playing' else '󰐊')
self.btn_play.set_name('mpris-btn')
self.btn_play.get_style_context().add_class('play')
self.btn_play.connect('clicked', self._on_play_pause)
btn_next = Gtk.Button(label='󰒭')
btn_next.set_name('mpris-btn')
btn_next.connect('clicked', lambda _: run(['playerctl', 'next']))
ctrl.pack_start(btn_prev, False, False, 0)
ctrl.pack_start(self.btn_play, False, False, 0)
ctrl.pack_start(btn_next, False, False, 0)
info_col.pack_start(ctrl, False, False, 0)
content.pack_start(info_col, True, True, 0)
box.pack_start(content, False, False, 4)
# Charger l'artwork en arrière-plan
if info.get('art_url'):
self.mpris_art.load_url(info['art_url'])
self._has_mpris = True
self._mpris_status = info['status']
def _on_play_pause(self, _btn):
run(['playerctl', 'play-pause'])
info = get_mpris_info()
if info and self._has_mpris:
self._mpris_status = info['status']
self.btn_play.set_label(
'󰏤' if info['status'] == 'playing' else '󰐊')
# ── Sélecteur de sortie ────────────────────────────────────────────────────
def _sink_selector(self, sinks):
self._sink_btns = {}
self._sink_descs = {}
col = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
for name, desc, is_default in sinks:
self._sink_descs[name] = desc
btn = Gtk.Button()
btn.set_name('device-btn')
btn.set_hexpand(True)
lbl = Gtk.Label(label=self._sink_label(name, desc, is_default))
lbl.set_halign(Gtk.Align.START)
btn.add(lbl)
if is_default:
btn.get_style_context().add_class('active')
btn.connect('clicked', self._on_sink_selected, name)
col.pack_start(btn, False, True, 0)
self._sink_btns[name] = btn
return col
def _sink_label(self, name, desc, active):
check = ' ' if active else ''
return f'{sink_icon(name, desc)} {desc}{check}'
def _source_selector(self, sources):
self._src_btns = {}
self._src_descs = {}
col = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
for name, desc, is_default in sources:
self._src_descs[name] = desc
btn = Gtk.Button()
btn.set_name('device-btn')
btn.set_hexpand(True)
lbl = Gtk.Label(label=self._src_label(name, desc, is_default))
lbl.set_halign(Gtk.Align.START)
btn.add(lbl)
if is_default:
btn.get_style_context().add_class('active')
btn.connect('clicked', self._on_source_selected, name)
col.pack_start(btn, False, True, 0)
self._src_btns[name] = btn
return col
def _src_label(self, name, desc, active):
check = ' ' if active else ''
return f'{source_icon(name, desc)} {desc}{check}'
def _on_source_selected(self, _btn, name):
for n, b in self._src_btns.items():
b.get_style_context().remove_class('active')
b.get_child().set_label(self._src_label(n, self._src_descs[n], False))
self._src_btns[name].get_style_context().add_class('active')
self._src_btns[name].get_child().set_label(
self._src_label(name, self._src_descs[name], True))
set_default_source(name)
self.src_device_lbl.set_label(self._src_descs[name])
vol, muted = get_source_volume()
self._blk = True self._blk = True
for scale in [self.sink_scale, self.src_scale, self.bright_scale]: self.src_scale.set_value(vol)
v = scale.get_value() self.src_pct.set_label(f'{vol}%')
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 self._blk = False
return False self._src_muted = muted
self._apply_mute(self.src_scale, self.src_icon, muted, mic_icon)
subprocess.Popen(['pkill', '-RTMIN+1', 'waybar'],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
def _on_sink_selected(self, _btn, name):
for n, b in self._sink_btns.items():
b.get_style_context().remove_class('active')
b.get_child().set_label(
self._sink_label(n, self._sink_descs[n], False))
self._sink_btns[name].get_style_context().add_class('active')
self._sink_btns[name].get_child().set_label(
self._sink_label(name, self._sink_descs[name], True))
set_default_sink(name)
self.sink_device_lbl.set_label(self._sink_descs[name])
vol, muted = get_sink_volume()
self._blk = True
self.sink_scale.set_value(vol)
self.sink_pct.set_label(f'{vol}%')
self._blk = False
self._sink_muted = muted
self._apply_mute(self.sink_scale, self.sink_icon, muted, vol_icon)
subprocess.Popen(['pkill', '-RTMIN+1', 'waybar'],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
# ── Builders UI ─────────────────────────────────────────────────────────── # ── Builders UI ───────────────────────────────────────────────────────────
@@ -376,7 +779,6 @@ class MediaPopup(Gtk.Window):
"""Retourne (row, scale, pct_label, icon_btn)""" """Retourne (row, scale, pct_label, icon_btn)"""
row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
# Icône = bouton mute
icon_btn = Gtk.Button(label=icon_char) icon_btn = Gtk.Button(label=icon_char)
icon_btn.set_name('mute-icon' if target == '@DEFAULT_AUDIO_SINK@' else 'mic-icon') icon_btn.set_name('mute-icon' if target == '@DEFAULT_AUDIO_SINK@' else 'mic-icon')
if muted: if muted:
@@ -384,7 +786,6 @@ class MediaPopup(Gtk.Window):
icon_btn.connect('clicked', mute_cb) icon_btn.connect('clicked', mute_cb)
row.pack_start(icon_btn, False, False, 0) row.pack_start(icon_btn, False, False, 0)
# Slider
scale = Gtk.Scale.new_with_range(Gtk.Orientation.HORIZONTAL, 0, 100, 5) scale = Gtk.Scale.new_with_range(Gtk.Orientation.HORIZONTAL, 0, 100, 5)
scale.set_value(val) scale.set_value(val)
scale.set_draw_value(False) scale.set_draw_value(False)
@@ -396,7 +797,6 @@ class MediaPopup(Gtk.Window):
self._on_audio_changed(s, pct_lbl, t)) self._on_audio_changed(s, pct_lbl, t))
row.pack_start(scale, True, True, 0) row.pack_start(scale, True, True, 0)
# %
pct_lbl = Gtk.Label(label=f'{val}%') pct_lbl = Gtk.Label(label=f'{val}%')
pct_lbl.set_name('pct-label') pct_lbl.set_name('pct-label')
pct_lbl.set_halign(Gtk.Align.END) pct_lbl.set_halign(Gtk.Align.END)
@@ -469,7 +869,20 @@ class MediaPopup(Gtk.Window):
self._apply_mute(self.src_scale, self.src_icon, self._apply_mute(self.src_scale, self.src_icon,
self._src_muted, mic_icon) self._src_muted, mic_icon)
def _redraw_scales(self):
"""Force GTK3 à recalculer les highlights."""
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))
scale.set_value(v)
self._blk = False
return False
def _refresh(self): def _refresh(self):
# ── Audio ──────────────────────────────────────────────────────────────
sink_vol, sink_muted = get_sink_volume() sink_vol, sink_muted = get_sink_volume()
src_vol, src_muted = get_source_volume() src_vol, src_muted = get_source_volume()
@@ -489,6 +902,20 @@ class MediaPopup(Gtk.Window):
self.src_pct.set_label(f'{src_vol}%') self.src_pct.set_label(f'{src_vol}%')
self._blk = False self._blk = False
# ── MPRIS ──────────────────────────────────────────────────────────────
if self._has_mpris:
info = get_mpris_info()
if info:
self.mpris_title.set_label(info['title'])
self.mpris_artist.set_label(info['artist'] or '')
if info['status'] != self._mpris_status:
self._mpris_status = info['status']
self.btn_play.set_label(
'󰏤' if info['status'] == 'playing' else '󰐊')
cur_url = info.get('art_url', '')
if cur_url != self.mpris_art._url:
self.mpris_art.load_url(cur_url)
return True return True

View File

@@ -1,422 +0,0 @@
#!/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

@@ -12,10 +12,35 @@ max_width = 600
max_height = 900 max_height = 900
[opener] [opener]
edit = [{ run = 'nano "$@"', block = true }] edit = [
{ run = '${EDITOR:-nano} "$@"', block = true },
]
open = [
{ run = 'xdg-open "$@"', orphan = true },
]
reveal = [
{ run = 'xdg-open "$(dirname "$1")"', orphan = true },
]
[open] [open]
rules = [ rules = [
{ mime = "text/*", use = "edit" }, { mime = "text/*", use = "edit" },
{ mime = "image/*", use = "open" }, { mime = "image/*", use = "open" },
{ mime = "video/*", use = "open" },
{ mime = "audio/*", use = "open" },
{ mime = "application/pdf", use = "open" },
{ mime = "application/json", use = "edit" },
{ mime = "*/xml", use = "edit" },
{ name = "*.md", use = "edit" },
{ name = "*.yml", use = "edit" },
{ name = "*.yaml", use = "edit" },
{ name = "*.toml", use = "edit" },
{ name = "*.conf", use = "edit" },
{ name = "*.sh", use = "edit" },
]
[plugin]
prepend_previewers = [
# Image preview via kitty protocol (native dans kitty)
{ mime = "image/*", run = "image" },
] ]

View File

@@ -17,19 +17,22 @@ zinit light zsh-users/zsh-completions
export PATH="/usr/local/bin:$HOME/.local/bin:/usr/bin:$HOME/.atuin/bin:$PATH" export PATH="/usr/local/bin:$HOME/.local/bin:/usr/bin:$HOME/.atuin/bin:$PATH"
# ── Historique ──────────────────────────────────────────────────────────────── # ── Historique ────────────────────────────────────────────────────────────────
HISTSIZE=10000 HISTSIZE=50000
HISTFILESIZE=20000 HISTFILESIZE=100000
HISTFILE=~/.zsh_history HISTFILE=~/.zsh_history
setopt HIST_IGNORE_DUPS setopt HIST_IGNORE_DUPS
setopt HIST_IGNORE_SPACE setopt HIST_IGNORE_SPACE
setopt HIST_FIND_NO_DUPS
setopt SHARE_HISTORY setopt SHARE_HISTORY
setopt APPEND_HISTORY setopt APPEND_HISTORY
setopt EXTENDED_HISTORY
# ── Options zsh ─────────────────────────────────────────────────────────────── # ── Options zsh ───────────────────────────────────────────────────────────────
setopt AUTO_CD # cd en tapant juste le nom du dossier setopt AUTO_CD # cd en tapant juste le nom du dossier
setopt CORRECT # correction de typos setopt CORRECT # correction de typos
setopt GLOB_DOTS # inclure fichiers cachés dans les globs setopt GLOB_DOTS # inclure fichiers caches dans les globs
setopt NO_BEEP # silence setopt NO_BEEP # silence
setopt INTERACTIVE_COMMENTS # commentaires en mode interactif
# ── Completion ──────────────────────────────────────────────────────────────── # ── Completion ────────────────────────────────────────────────────────────────
autoload -Uz compinit && compinit autoload -Uz compinit && compinit
@@ -37,44 +40,46 @@ zstyle ':completion:*' menu select
zstyle ':completion:*' list-colors "${(s.:.)LS_COLORS}" zstyle ':completion:*' list-colors "${(s.:.)LS_COLORS}"
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Za-z}' zstyle ':completion:*' matcher-list 'm:{a-z}={A-Za-z}'
zstyle ':completion:*' group-name '' zstyle ':completion:*' group-name ''
zstyle ':completion:*:descriptions' format '%F{#e79cfe}── %d ──%f' zstyle ':completion:*:descriptions' format '%F{#c9a0ff}── %d ──%f'
# ── Couleurs grep ───────────────────────────────────────────────────────────── # ── Couleurs grep ─────────────────────────────────────────────────────────────
alias grep='grep --color=auto' alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto' alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto' alias egrep='egrep --color=auto'
# ── eza (remplace ls) ──────────────────────────────────────────────────────── # ── eza (remplace ls) ────────────────────────────────────────────────────────
alias ls='eza --icons --color=always --group-directories-first' alias ls='eza --icons --color=always --group-directories-first'
alias ll='eza -l --icons --git --group-directories-first' alias ll='eza -l --icons --git --group-directories-first'
alias lt='eza --tree --icons' alias la='eza -la --icons --git --group-directories-first'
alias lt='eza --tree --icons --level=3'
export LS_COLORS="$(bash ~/.local/share/violet-chaton/violet-chaton-ls-colors.sh)" export LS_COLORS="$(bash ~/.local/share/violet-chaton/violet-chaton-ls-colors.sh)"
# ── bat (remplace cat) ─────────────────────────────────────────────────────── # ── bat (remplace cat) ───────────────────────────────────────────────────────
alias bat='batcat' alias bat='batcat'
alias cat='batcat --paging=never' alias cat='batcat --paging=never'
export MANPAGER="sh -c 'col -bx | batcat -l man -p'" export MANPAGER="sh -c 'col -bx | batcat -l man -p'"
# ── fd (remplace find) ─────────────────────────────────────────────────────── # ── fd (remplace find) ───────────────────────────────────────────────────────
alias fd='fdfind' alias fd='fdfind'
# ── Nouveaux outils ─────────────────────────────────────────────────────────── # ── Outils modernes ──────────────────────────────────────────────────────────
alias lg='lazygit' # git TUI alias lg='lazygit'
alias rg='rg --color=always' # ripgrep avec couleurs alias rg='rg --color=always'
alias disk='ncdu' # analyse disque alias disk='dust' # analyse disque (remplace ncdu)
alias man='tldr' # man pages simplifiées alias tl='tldr' # man pages simplifiees (man reste intact)
alias pipes='pipes.sh' # animation pipes alias pipes='pipes.sh'
fuck() { eval "$(thefuck --alias 2>/dev/null)"; fuck "$@"; } # init paresseux alias top='btop'
fuck() { eval "$(thefuck --alias 2>/dev/null)"; fuck "$@"; }
# ── fzf ────────────────────────────────────────────────────────────────────── # ── fzf ──────────────────────────────────────────────────────────────────────
[ -f /usr/share/doc/fzf/examples/key-bindings.zsh ] && source /usr/share/doc/fzf/examples/key-bindings.zsh [ -f /usr/share/doc/fzf/examples/key-bindings.zsh ] && source /usr/share/doc/fzf/examples/key-bindings.zsh
[ -f /usr/share/doc/fzf/examples/completion.zsh ] && source /usr/share/doc/fzf/examples/completion.zsh [ -f /usr/share/doc/fzf/examples/completion.zsh ] && source /usr/share/doc/fzf/examples/completion.zsh
# Raccourcis fzf personnalisés # Raccourcis fzf
bindkey -r '^T' # libère Ctrl+T bindkey -r '^T'
bindkey -r '\ec' # libère Alt+C bindkey -r '\ec'
bindkey '^G' fzf-file-widget # Ctrl+G → chercher un fichier bindkey '^G' fzf-file-widget
bindkey '^F' fzf-cd-widget # Ctrl+F → chercher un dossier bindkey '^F' fzf-cd-widget
export FZF_DEFAULT_COMMAND='fdfind --type f --hidden --follow --exclude .git --exclude .cache --search-path $HOME' export FZF_DEFAULT_COMMAND='fdfind --type f --hidden --follow --exclude .git --exclude .cache --search-path $HOME'
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND" export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
export FZF_ALT_C_COMMAND='fdfind --type d --hidden --follow --exclude .git --exclude .cache --search-path $HOME' export FZF_ALT_C_COMMAND='fdfind --type d --hidden --follow --exclude .git --exclude .cache --search-path $HOME'
@@ -82,39 +87,188 @@ export FZF_DEFAULT_OPTS="
--height 40% --layout=reverse --border rounded --info=hidden --height 40% --layout=reverse --border rounded --info=hidden
--preview 'batcat --color=always --style=plain --line-range=:80 {}' --preview 'batcat --color=always --style=plain --line-range=:80 {}'
--preview-window 'right:50%:wrap' --preview-window 'right:50%:wrap'
--color=bg+:#3d2454,bg:#341c4a,spinner:#ff79c6,hl:#8be9fd --color=bg+:#5a3875,bg:#261537,spinner:#ff4da6,hl:#a4b4ff
--color=fg:#f8f8f2,header:#ff79c6,info:#e79cfe,pointer:#ff79c6 --color=fg:#f0eaf8,header:#ff4da6,info:#c9a0ff,pointer:#ff4da6
--color=marker:#a6e3a1,fg+:#f8f8f2,prompt:#e79cfe,hl+:#8be9fd" --color=marker:#9adba8,fg+:#f0eaf8,prompt:#c9a0ff,hl+:#a4b4ff"
# ── zoxide (remplace cd) ────────────────────────────────────────────────────── # ── zoxide (remplace cd) ────────────────────────────────────────────────────
eval "$(zoxide init zsh --cmd cd)" eval "$(zoxide init zsh --cmd cd)"
# ── Fastfetch ───────────────────────────────────────────────────────────────── # ── direnv (charge .envrc automatiquement) ───────────────────────────────────
alias fetch='fastfetch --config ~/.config/fastfetch/config.jsonc --raw <(chafa --size 44x22 --symbols vhalf --color-space rgb "$HOME/.config/fastfetch/violet-chaton-logo.png") --logo-width 52' command -v direnv &>/dev/null && eval "$(direnv hook zsh)"
fetch
# ── zsh-autosuggestions ─────────────────────────────────────────────────────── # ── fnm (Node.js version manager — lazy load) ───────────────────────────────
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=#6c7086" if [ -d "$HOME/.local/share/fnm" ]; then
export PATH="$HOME/.local/share/fnm:$PATH"
eval "$(fnm env --use-on-cd --shell zsh)"
fi
# ── Fastfetch ────────────────────────────────────────────────────────────────
alias fetch='fastfetch --config ~/.config/fastfetch/config.jsonc --raw <(chafa --size 44x22 --symbols vhalf --color-space rgb "$HOME/.config/fastfetch/violet-chaton-logo.png") --logo-width 52'
[[ -z "$TMUX" && -z "$INSIDE_EMACS" && -z "$VSCODE_INJECTION" ]] && fetch
# ── zsh-autosuggestions ──────────────────────────────────────────────────────
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=#716686"
ZSH_AUTOSUGGEST_STRATEGY=(history completion) ZSH_AUTOSUGGEST_STRATEGY=(history completion)
ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=20 ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=20
bindkey '^ ' autosuggest-accept # Ctrl+Space pour accepter la suggestion bindkey '^ ' autosuggest-accept
# ── zsh-syntax-highlighting (couleurs violet-chaton) ───────────────────────── # ── zsh-syntax-highlighting (couleurs violet-chaton v2) ──────────────────────
ZSH_HIGHLIGHT_STYLES[command]='fg=#8be9fd' ZSH_HIGHLIGHT_STYLES[command]='fg=#a4b4ff'
ZSH_HIGHLIGHT_STYLES[builtin]='fg=#ff79c6' ZSH_HIGHLIGHT_STYLES[builtin]='fg=#ff4da6'
ZSH_HIGHLIGHT_STYLES[alias]='fg=#e79cfe' ZSH_HIGHLIGHT_STYLES[alias]='fg=#c9a0ff'
ZSH_HIGHLIGHT_STYLES[function]='fg=#8be9fd' ZSH_HIGHLIGHT_STYLES[function]='fg=#a4b4ff'
ZSH_HIGHLIGHT_STYLES[string]='fg=#e79cfe' ZSH_HIGHLIGHT_STYLES[string]='fg=#9adba8'
ZSH_HIGHLIGHT_STYLES[path]='fg=#f8f8f2,underline' ZSH_HIGHLIGHT_STYLES[path]='fg=#f0eaf8,underline'
ZSH_HIGHLIGHT_STYLES[unknown-token]='fg=#f38ba8,bold' ZSH_HIGHLIGHT_STYLES[unknown-token]='fg=#f25c7a,bold,standout'
ZSH_HIGHLIGHT_STYLES[reserved-word]='fg=#ff79c6' ZSH_HIGHLIGHT_STYLES[reserved-word]='fg=#ff4da6'
ZSH_HIGHLIGHT_STYLES[comment]='fg=#6c7086,italic' ZSH_HIGHLIGHT_STYLES[single-hyphen-option]='fg=#e8c87a'
ZSH_HIGHLIGHT_STYLES[globbing]='fg=#f9e2af' ZSH_HIGHLIGHT_STYLES[double-hyphen-option]='fg=#e8c87a'
ZSH_HIGHLIGHT_STYLES[history-expansion]='fg=#f9e2af' ZSH_HIGHLIGHT_STYLES[back-quoted-argument]='fg=#c9a0ff'
ZSH_HIGHLIGHT_STYLES[dollar-quoted-argument]='fg=#9adba8'
ZSH_HIGHLIGHT_STYLES[assign]='fg=#f0eaf8'
ZSH_HIGHLIGHT_STYLES[redirection]='fg=#ff4da6,bold'
ZSH_HIGHLIGHT_STYLES[commandseparator]='fg=#ff4da6'
# ── atuin (remplace Ctrl+R) ─────────────────────────────────────────────────── # ── Vi-mode — cursor shape adaptatif ────────────────────────────────────────
bindkey -v
export KEYTIMEOUT=1
# Beam en insert, block en normal
zle-keymap-select() {
case $KEYMAP in
vicmd) echo -ne '\e[1 q' ;; # block clignotant
viins|main) echo -ne '\e[5 q' ;; # beam clignotant
esac
}
zle -N zle-keymap-select
# Reset beam au demarrage de chaque prompt
zle-line-init() { echo -ne '\e[5 q' }
zle -N zle-line-init
# Garder les keybindings essentiels en mode insert
bindkey '^A' beginning-of-line
bindkey '^E' end-of-line
bindkey '^K' kill-line
bindkey '^U' backward-kill-line
bindkey '^W' backward-kill-word
bindkey '^Y' yank
bindkey '^R' history-incremental-search-backward
bindkey '^P' up-line-or-history
bindkey '^N' down-line-or-history
bindkey '^?' backward-delete-char
bindkey '^H' backward-delete-char
ZSH_HIGHLIGHT_STYLES[comment]='fg=#716686,italic'
ZSH_HIGHLIGHT_STYLES[globbing]='fg=#e8c87a'
ZSH_HIGHLIGHT_STYLES[history-expansion]='fg=#e8c87a'
# ── fzf pimped — git log picker, process killer ─────────────────────────────
# Git log browser — Ctrl+L en mode normal
fzf-git-log() {
git log --oneline --color=always --decorate --graph 2>/dev/null |
fzf --ansi --no-sort --reverse --preview \
'git show --color=always $(echo {} | grep -oP "[a-f0-9]{7,}" | head -1)' \
--preview-window='right:60%:wrap' \
--bind='enter:execute(echo {} | grep -oP "[a-f0-9]{7,}" | head -1 | tr -d "\n" | xclip -selection clipboard)+abort'
}
alias glog='fzf-git-log'
# Process killer — fzf powered
fkill() {
local pid
pid=$(procs --color=always 2>/dev/null || ps aux) |
fzf --ansi --header="Select process to kill" \
--preview-window='hidden' |
awk '{print $1}'
[ -n "$pid" ] && kill -9 "$pid" && echo "Killed $pid"
}
# ── Shell functions — violet-chaton toolkit ──────────────────────────────────
# mkcd — creer un dossier et y aller
mkcd() { mkdir -p "$1" && cd "$1" }
# Project switcher — fzf dans ~/Dev
proj() {
local dir
dir=$(find ~/Dev -maxdepth 3 -name ".git" -type d 2>/dev/null | sed 's|/.git$||' |
fzf --preview 'eza -la --icons --color=always --group-directories-first {}' \
--preview-window='right:50%')
[ -n "$dir" ] && cd "$dir"
}
# Palette preview — affiche violet-chaton v2 dans le terminal
colors() {
echo ""
echo -e " \033[1mviolet-chaton v2\033[0m"
echo ""
echo -e " \033[38;2;255;77;166m████\033[0m magenta #ff4da6"
echo -e " \033[38;2;201;160;255m████\033[0m lilac #c9a0ff"
echo -e " \033[38;2;154;219;168m████\033[0m mitsuri #9adba8"
echo -e " \033[38;2;164;180;255m████\033[0m lavande #a4b4ff"
echo -e " \033[38;2;232;200;122m████\033[0m champagne #e8c87a"
echo ""
echo -e " \033[38;2;242;92;122m████\033[0m danger #f25c7a"
echo -e " \033[38;2;232;200;122m████\033[0m warning #e8c87a"
echo -e " \033[38;2;154;219;168m████\033[0m success #9adba8"
echo -e " \033[38;2;164;180;255m████\033[0m info #a4b4ff"
echo ""
echo -e " \033[38;2;240;234;248m████\033[0m text #f0eaf8"
echo -e " \033[38;2;196;184;212m████\033[0m subtext1 #c4b8d4"
echo -e " \033[38;2;154;143;173m████\033[0m subtext0 #9a8fad"
echo -e " \033[38;2;113;102;134m████\033[0m muted #716686"
echo ""
echo -e " Gradient: \033[38;2;255;77;166m██\033[38;2;226;118;212m██\033[38;2;201;160;255m██\033[38;2;182;168;255m██\033[38;2;164;180;255m██\033[38;2;159;200;212m██\033[38;2;154;219;168m██\033[38;2;130;232;160m██\033[0m"
echo ""
}
# Yazi — wrapper qui cd dans le dernier dossier visite
y() {
local tmp="$(mktemp -t "yazi-cwd.XXXXXX")"
yazi "$@" --cwd-file="$tmp"
if cwd="$(cat -- "$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then
cd "$cwd"
fi
rm -f "$tmp"
}
# Weather — meteo rapide
weather() { curl -s "wttr.in/${1:-Paris}?format=3" }
# Cheatsheet — raccourcis violet-chaton
hotkeys() {
echo ""
echo -e " \033[38;2;255;77;166m\033[1m── kitty ──\033[0m"
echo -e " \033[38;2;201;160;255mCtrl+Shift+\\\033[0m split vertical"
echo -e " \033[38;2;201;160;255mCtrl+Shift+-\033[0m split horizontal"
echo -e " \033[38;2;201;160;255mCtrl+Shift+Z\033[0m zoom (stack toggle)"
echo -e " \033[38;2;201;160;255mCtrl+Shift+←→↑↓\033[0m naviguer splits"
echo -e " \033[38;2;201;160;255mCtrl+Shift+1-5\033[0m layouts (tall/fat/grid/h/v)"
echo -e " \033[38;2;201;160;255mCtrl+Shift+T\033[0m nouvel onglet"
echo ""
echo -e " \033[38;2;255;77;166m\033[1m── zsh ──\033[0m"
echo -e " \033[38;2;154;219;168mEsc\033[0m vi normal mode (hjkl, w, b)"
echo -e " \033[38;2;154;219;168mi\033[0m retour insert"
echo -e " \033[38;2;154;219;168mCtrl+G\033[0m fzf fichier"
echo -e " \033[38;2;154;219;168mCtrl+F\033[0m fzf dossier"
echo -e " \033[38;2;154;219;168mCtrl+Space\033[0m accepter suggestion"
echo ""
echo -e " \033[38;2;255;77;166m\033[1m── commandes ──\033[0m"
echo -e " \033[38;2;164;180;255mproj\033[0m project switcher fzf"
echo -e " \033[38;2;164;180;255mglog\033[0m git log fzf"
echo -e " \033[38;2;164;180;255mfkill\033[0m process killer fzf"
echo -e " \033[38;2;164;180;255my\033[0m yazi (cd au dossier visite)"
echo -e " \033[38;2;164;180;255mcolors\033[0m palette violet-chaton v2"
echo -e " \033[38;2;164;180;255mweather [ville]\033[0m meteo rapide"
echo -e " \033[38;2;164;180;255mhotkeys\033[0m cette aide"
echo ""
}
# ── atuin (remplace Ctrl+R) ──────────────────────────────────────────────────
[[ -f ~/.atuin/bin/env ]] && source ~/.atuin/bin/env [[ -f ~/.atuin/bin/env ]] && source ~/.atuin/bin/env
command -v atuin &>/dev/null && eval "$(atuin init zsh --disable-up-arrow)" command -v atuin &>/dev/null && eval "$(atuin init zsh --disable-up-arrow)"
# ── starship ───────────────────────────────────────────────────────────────── # ── starship ─────────────────────────────────────────────────────────────────
command -v starship &>/dev/null && eval "$(starship init zsh)" command -v starship &>/dev/null && eval "$(starship init zsh)"

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" 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 ────────────────────────────────────────────────── # ── Vérifications préalables ──────────────────────────────────────────────────
check_requirements() { check_requirements() {
local ok=true local ok=true

View File

@@ -19,6 +19,7 @@ PACKAGES=(
chafa chafa
jq jq
libgtk-3-bin libgtk-3-bin
adw-gtk3
nemo nemo
nemo-fileroller nemo-fileroller
# fastfetch → installé via .deb GitHub (voir 02-packages-manual.sh) # fastfetch → installé via .deb GitHub (voir 02-packages-manual.sh)
@@ -26,16 +27,19 @@ PACKAGES=(
btop btop
# atuin → installé via son propre script (voir 02-packages-manual.sh) # atuin → installé via son propre script (voir 02-packages-manual.sh)
# starship → 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 ────────────────────────────────────── # ── AGS + deps (remplace waybar/wob/wofi) ─────────────────────────────────
waybar
wob
wofi
brightnessctl brightnessctl
playerctl playerctl
wireplumber wireplumber
python3-gi
gir1.2-gtk-3.0 gir1.2-gtk-3.0
gir1.2-gtklayershell-0.1 gir1.2-gtklayershell-0.1
gir1.2-gdkpixbuf-2.0
gir1.2-pango-1.0
libgtk-layer-shell-dev
libpulse-dev
libdbusmenu-gtk3-dev
# Rofi reste en backup launcher (AGS launcher = principal)
rofi
# ── Fun & utils ────────────────────────────────────────────────────────── # ── Fun & utils ──────────────────────────────────────────────────────────
cmatrix cmatrix
toilet toilet

View File

@@ -131,6 +131,135 @@ else
chmod +x "$BIN/pipes.sh" && ok "pipes.sh" || fail "pipes.sh" chmod +x "$BIN/pipes.sh" && ok "pipes.sh" || fail "pipes.sh"
fi fi
# dust (remplace ncdu/du)
if has_cmd dust; then
ok "dust (déjà installé)"
else
DUST_VER=$(github_latest_tag "bootandy/dust")
if [ -z "$DUST_VER" ]; then
fail "dust — version introuvable"
else
install_binary "dust" \
"https://github.com/bootandy/dust/releases/download/${DUST_VER}/dust-${DUST_VER}-x86_64-unknown-linux-musl.tar.gz" \
"tar:dust"
fi
fi
# procs (remplace ps)
if has_cmd procs; then
ok "procs (déjà installé)"
else
PROCS_VER=$(github_latest_tag "dalance/procs")
if [ -z "$PROCS_VER" ]; then
fail "procs — version introuvable"
else
install_binary "procs" \
"https://github.com/dalance/procs/releases/download/${PROCS_VER}/procs-${PROCS_VER}-x86_64-linux.zip" \
"zip:procs"
fi
fi
# tokei (stats code par langage) — v12 car v13+ n'a plus de binaires pre-compiles
if has_cmd tokei; then
ok "tokei (déjà installé)"
else
install_binary "tokei" \
"https://github.com/XAMPPRocky/tokei/releases/download/v12.1.2/tokei-x86_64-unknown-linux-musl.tar.gz" \
"tar:tokei"
fi
# sd (remplace sed simplifié)
if has_cmd sd; then
ok "sd (déjà installé)"
else
SD_VER=$(github_latest_tag "chmln/sd")
if [ -z "$SD_VER" ]; then
fail "sd — version introuvable"
else
install_binary "sd" \
"https://github.com/chmln/sd/releases/download/${SD_VER}/sd-${SD_VER}-x86_64-unknown-linux-musl.tar.gz" \
"tar:sd"
fi
fi
# hyperfine (benchmarks CLI)
if has_cmd hyperfine; then
ok "hyperfine (déjà installé)"
else
HF_VER=$(github_latest_tag "sharkdp/hyperfine")
if [ -z "$HF_VER" ]; then
fail "hyperfine — version introuvable"
else
install_binary "hyperfine" \
"https://github.com/sharkdp/hyperfine/releases/download/${HF_VER}/hyperfine-${HF_VER}-x86_64-unknown-linux-musl.tar.gz" \
"tar:hyperfine"
fi
fi
# gping (ping avec graphe)
if has_cmd gping; then
ok "gping (déjà installé)"
else
GPING_VER=$(github_latest_tag "orf/gping")
if [ -z "$GPING_VER" ]; then
fail "gping — version introuvable"
else
install_binary "gping" \
"https://github.com/orf/gping/releases/download/${GPING_VER}/gping-Linux-musl-x86_64.tar.gz" \
"tar:gping"
fi
fi
section "AGS (Aylur's GTK Shell — barre + OSD + launcher + notifs)"
if has_cmd ags; then
ok "ags (deja installe)"
else
step "Installation de AGS depuis source..."
tmp=$(mktemp -d)
AGS_VER=$(github_latest_tag "Aylur/ags")
if [ -z "$AGS_VER" ]; then
warn "ags — version introuvable — a installer manuellement"
else
if git clone --depth 1 --branch "$AGS_VER" https://github.com/Aylur/ags.git "$tmp/ags" &>/dev/null; then
if cd "$tmp/ags" && npm install &>/dev/null && npm run build &>/dev/null; then
cp "$tmp/ags/ags" "$BIN/ags" 2>/dev/null && chmod +x "$BIN/ags" 2>/dev/null
if [ -x "$BIN/ags" ]; then
ok "ags"
else
warn "ags — build OK mais binaire non trouve. Install manuelle recommandee :"
info " git clone https://github.com/Aylur/ags && cd ags && npm i && npm run build"
fi
else
warn "ags — build echoue. Install manuelle recommandee :"
info " git clone https://github.com/Aylur/ags && cd ags && npm i && npm run build"
fi
cd - &>/dev/null
else
warn "ags — clone echoue"
fi
fi
rm -rf "$tmp"
fi
section "kitty (terminal GPU-accelerated)"
if has_cmd kitty; then
ok "kitty (deja installe)"
else
step "Installation de kitty..."
if curl -L https://sw.kovidgoyal.net/kitty/installer.sh | sh /dev/stdin launch=n &>/dev/null; then
ln -sf "$HOME/.local/kitty.app/bin/kitty" "$HOME/.local/bin/kitty"
ln -sf "$HOME/.local/kitty.app/bin/kitten" "$HOME/.local/bin/kitten"
# Desktop file pour le launcher
cp "$HOME/.local/kitty.app/share/applications/kitty.desktop" \
"$HOME/.local/share/applications/" 2>/dev/null
sed -i "s|Icon=kitty|Icon=$HOME/.local/kitty.app/share/icons/hicolor/256x256/apps/kitty.png|g" \
"$HOME/.local/share/applications/kitty.desktop" 2>/dev/null
ok "kitty"
else
fail "kitty"
fi
fi
section "starship (prompt)" section "starship (prompt)"
if has_cmd starship; then if has_cmd starship; then
ok "starship (déjà installé)" ok "starship (déjà installé)"
@@ -188,7 +317,7 @@ else
fi fi
# ── Nerd Fonts ───────────────────────────────────────────────────────────────── # ── Nerd Fonts ─────────────────────────────────────────────────────────────────
section "Nerd Fonts (JetBrainsMono NL + 0xProto)" section "Nerd Fonts (Maple Mono NF + JetBrainsMono NL + 0xProto)"
FONTS_DIR="$HOME/.local/share/fonts/NerdFonts" FONTS_DIR="$HOME/.local/share/fonts/NerdFonts"
ensure_dir "$FONTS_DIR" ensure_dir "$FONTS_DIR"
@@ -213,6 +342,9 @@ install_font() {
rm -rf "$tmp" rm -rf "$tmp"
} }
install_font "Maple Mono NF" \
"https://github.com/subframe7536/maple-font/releases/latest/download/MapleMono-NF.zip" \
"MapleMono-NF-Regular.ttf"
install_font "JetBrainsMono NL" \ install_font "JetBrainsMono NL" \
"https://github.com/ryanoasis/nerd-fonts/releases/latest/download/JetBrainsMono.zip" \ "https://github.com/ryanoasis/nerd-fonts/releases/latest/download/JetBrainsMono.zip" \
"JetBrainsMonoNLNerdFont-Regular.ttf" "JetBrainsMonoNLNerdFont-Regular.ttf"

View File

@@ -11,7 +11,14 @@ deploy_file() {
local src="$1" local src="$1"
local dst="$2" local dst="$2"
ensure_dir "$(dirname "$dst")" 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/"}" local rel="${dst#"$HOME/"}"
ensure_dir "$BACKUP_DIR/$(dirname "$rel")" ensure_dir "$BACKUP_DIR/$(dirname "$rel")"
cp "$dst" "$BACKUP_DIR/$rel" 2>/dev/null cp "$dst" "$BACKUP_DIR/$rel" 2>/dev/null
@@ -44,6 +51,10 @@ fi
# ── Configs outils ───────────────────────────────────────────────────────────── # ── Configs outils ─────────────────────────────────────────────────────────────
section "Configs outils" section "Configs outils"
deploy_file "$CONFIGS/starship.toml" "$HOME/.config/starship.toml" deploy_file "$CONFIGS/starship.toml" "$HOME/.config/starship.toml"
deploy_file "$CONFIGS/kitty.conf" "$HOME/.config/kitty/kitty.conf"
if [ -f "$CONFIGS/kitty/violet-chaton-glow.glsl" ]; then
deploy_file "$CONFIGS/kitty/violet-chaton-glow.glsl" "$HOME/.config/kitty/violet-chaton-glow.glsl"
fi
deploy_file "$CONFIGS/bat.conf" "$HOME/.config/bat/config" deploy_file "$CONFIGS/bat.conf" "$HOME/.config/bat/config"
deploy_file "$CONFIGS/btop.conf" "$HOME/.config/btop/btop.conf" deploy_file "$CONFIGS/btop.conf" "$HOME/.config/btop/btop.conf"
deploy_file "$CONFIGS/fastfetch.jsonc" "$HOME/.config/fastfetch/config.jsonc" deploy_file "$CONFIGS/fastfetch.jsonc" "$HOME/.config/fastfetch/config.jsonc"
@@ -118,11 +129,33 @@ else
fail "CosmicTerm" fail "CosmicTerm"
fi fi
# ── GTK3 — thème violet-chaton ───────────────────────────────────────────── # ── GTK3 / GTK4 — thème violet-chaton ─────────────────────────────────────
section "GTK3 — thème violet-chaton" section "GTK — thème violet-chaton"
step "Thème GTK3 dark (adw-gtk3-dark + couleurs violet-chaton)..."
ensure_dir "$HOME/.config/gtk-3.0" ensure_dir "$HOME/.config/gtk-3.0"
deploy_file "$THEMES/violet-chaton-gtk.css" "$HOME/.config/gtk-3.0/gtk.css" deploy_file "$THEMES/violet-chaton-gtk.css" "$HOME/.config/gtk-3.0/gtk.css"
step "Thème GTK3 light..."
deploy_file "$THEMES/violet-chaton-gtk-light.css" "$HOME/.config/gtk-3.0/gtk-light.css"
step "Thème GTK4 / libadwaita dark..."
ensure_dir "$HOME/.config/gtk-4.0"
deploy_file "$THEMES/violet-chaton-gtk.css" "$HOME/.config/gtk-4.0/gtk.css"
step "Thème GTK4 / libadwaita light..."
deploy_file "$THEMES/violet-chaton-gtk-light.css" "$HOME/.config/gtk-4.0/gtk-light.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 ──────────────────────────────────────── # ── Nemo — gestionnaire de fichiers ────────────────────────────────────────
section "Nemo — configuration et thème" section "Nemo — configuration et thème"
@@ -223,105 +256,59 @@ PYEOF
fi fi
fi fi
# ── Waybar ───────────────────────────────────────────────────────────────────── # ── AGS (remplace waybar + wob + wofi) ─────────────────────────────────────────
section "Waybar — island floating 3 pills" section "AGS — violet-chaton shell"
deploy_file "$CONFIGS/waybar/config" "$HOME/.config/waybar/config" ensure_dir "$HOME/.config/ags/css"
deploy_file "$THEMES/violet-chaton-waybar.css" "$HOME/.config/waybar/style.css" ensure_dir "$HOME/.config/ags/widgets"
deploy_file "$CONFIGS/waybar/cava-waybar.cfg" "$HOME/.config/waybar/cava-waybar.cfg" deploy_file "$CONFIGS/ags/config.js" "$HOME/.config/ags/config.js"
deploy_file "$CONFIGS/ags/css/style.css" "$HOME/.config/ags/css/style.css"
deploy_file "$CONFIGS/ags/widgets/Bar.js" "$HOME/.config/ags/widgets/Bar.js"
deploy_file "$CONFIGS/ags/widgets/OSD.js" "$HOME/.config/ags/widgets/OSD.js"
deploy_file "$CONFIGS/ags/widgets/Launcher.js" "$HOME/.config/ags/widgets/Launcher.js"
deploy_file "$CONFIGS/ags/widgets/Notifications.js" "$HOME/.config/ags/widgets/Notifications.js"
step "Scripts waybar..." # ── Autostart — AGS ────────────────────────────────────────────────────────────
ensure_dir "$HOME/.config/waybar/scripts" section "Autostart — AGS"
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" 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" cat > /tmp/ags-vc.desktop << 'DESKTOP'
[Desktop Entry]
Type=Application
Name=AGS violet-chaton
Exec=ags
X-GNOME-Autostart-enabled=true
DESKTOP
deploy_file "/tmp/ags-vc.desktop" "$HOME/.config/autostart/ags.desktop"
rm -f /tmp/ags-vc.desktop
# Cleanup ancien waybar/wob autostart si present
[ -f "$HOME/.config/autostart/waybar.desktop" ] && rm -f "$HOME/.config/autostart/waybar.desktop" && ok "waybar autostart retire"
[ -f "$HOME/.config/autostart/wob.desktop" ] && rm -f "$HOME/.config/autostart/wob.desktop" && ok "wob autostart retire"
# ── Règle sudoers — profil énergie ────────────────────────────────────────────── # ── Règle sudoers — profil énergie ──────────────────────────────────────────────
section "Sudoers — profil énergie ACPI" section "Sudoers — profil énergie ACPI"
SUDOERS_FILE="/etc/sudoers.d/waybar-power-profile" SUDOERS_FILE="/etc/sudoers.d/vc-power-profile"
SUDOERS_RULE="$USER ALL=(ALL) NOPASSWD: /usr/bin/tee /sys/firmware/acpi/platform_profile" SUDOERS_RULE="$USER ALL=(ALL) NOPASSWD: /usr/bin/tee /sys/firmware/acpi/platform_profile"
if [ -f "$SUDOERS_FILE" ]; then if [ -f "$SUDOERS_FILE" ] || [ -f "/etc/sudoers.d/waybar-power-profile" ]; then
ok "Règle sudoers déjà présente" ok "Règle sudoers déjà présente"
else else
step "Création de la règle sudoers (mot de passe sudo requis)..." step "Création de la règle sudoers (mot de passe sudo requis)..."
echo "$SUDOERS_RULE" > /tmp/waybar-pp-sudoers echo "$SUDOERS_RULE" > /tmp/vc-pp-sudoers
if sudo cp /tmp/waybar-pp-sudoers "$SUDOERS_FILE" && sudo chmod 440 "$SUDOERS_FILE"; then if sudo cp /tmp/vc-pp-sudoers "$SUDOERS_FILE" && sudo chmod 440 "$SUDOERS_FILE"; then
rm -f /tmp/waybar-pp-sudoers rm -f /tmp/vc-pp-sudoers
ok "Règle sudoers créée" ok "Règle sudoers créée"
else else
rm -f /tmp/waybar-pp-sudoers rm -f /tmp/vc-pp-sudoers
warn "Échec sudoers — changement de profil énergie nécessitera sudo" warn "Échec sudoers — changement de profil énergie nécessitera sudo"
fi fi
fi fi
# ── Règle udev — permissions platform_profile ──────────────────────────────── # ── Rofi — backup launcher (AGS launcher = principal) ──────────────────────────
section "udev — platform_profile accessible au groupe video" section "Rofi — thème violet-chaton (backup launcher)"
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" ensure_dir "$HOME/.config/rofi"
deploy_file "$CONFIGS/rofi/config.rasi" "$HOME/.config/rofi/config.rasi" deploy_file "$CONFIGS/rofi/config.rasi" "$HOME/.config/rofi/config.rasi"
deploy_file "$THEMES/violet-chaton-rofi.rasi" "$HOME/.config/rofi/violet-chaton.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 ───────────────────────────────────────────────────────────── # ── Logo fastfetch ─────────────────────────────────────────────────────────────
section "Logo fastfetch" section "Logo fastfetch"
if [ -f "$SCRIPT_DIR/assets/violet-chaton-logo.png" ]; then if [ -f "$SCRIPT_DIR/assets/violet-chaton-logo.png" ]; then

View File

@@ -1,16 +1,23 @@
#!/bin/bash #!/bin/bash
# ── violet-chaton : fonctions partagées ─────────────────────────────────────── # ── violet-chaton : fonctions partagées ───────────────────────────────────────
PINK='\033[38;2;255;121;198m' # ── Palette violet-chaton v2 (source de verite : themes/palette.sh) ──────────
PURPLE='\033[38;2;231;156;254m' _LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CYAN='\033[38;2;139;233;253m' _PALETTE="$_LIB_DIR/../themes/palette.sh"
GREEN='\033[38;2;166;227;161m' if [ -f "$_PALETTE" ]; then
YELLOW='\033[38;2;249;226;175m' source "$_PALETTE"
RED='\033[38;2;243;139;168m' fi
MUTED='\033[38;2;108;112;134m'
TEXT='\033[38;2;248;248;242m' PINK="${VC_ANSI_MAGENTA:-\033[38;2;255;77;166m}"
BOLD='\033[1m' PURPLE="${VC_ANSI_LILAC:-\033[38;2;201;160;255m}"
RESET='\033[0m' CYAN="${VC_ANSI_LAVANDE:-\033[38;2;164;180;255m}"
GREEN="${VC_ANSI_MITSURI:-\033[38;2;154;219;168m}"
YELLOW="${VC_ANSI_CHAMPAGNE:-\033[38;2;232;200;122m}"
RED="${VC_ANSI_DANGER:-\033[38;2;242;92;122m}"
MUTED="${VC_ANSI_MUTED:-\033[38;2;113;102;134m}"
TEXT="${VC_ANSI_TEXT:-\033[38;2;240;234;248m}"
BOLD="${VC_ANSI_BOLD:-\033[1m}"
RESET="${VC_ANSI_RESET:-\033[0m}"
# ── Log ──────────────────────────────────────────────────────────────────────── # ── Log ────────────────────────────────────────────────────────────────────────
# INSTALL_LOG peut être pré-exporté par install.sh pour que tous les sous-scripts # INSTALL_LOG peut être pré-exporté par install.sh pour que tous les sous-scripts

View File

@@ -1,40 +1,40 @@
{ {
0: ( 0: (
name: "violet-chaton", name: "violet-chaton",
foreground: "#F8F8F2", foreground: "#F0EAF8",
background: "#261537", background: "#261537",
cursor: "#FF79C6", cursor: "#FF4DA6",
bright_foreground: "#F8F8F2", bright_foreground: "#F0EAF8",
dim_foreground: "#6C7086", dim_foreground: "#716686",
normal: ( normal: (
black: "#261537", black: "#261537",
red: "#F38BA8", red: "#F25C7A",
green: "#A6E3A1", green: "#9ADBA8",
yellow: "#F9E2AF", yellow: "#E8C87A",
blue: "#3D2454", blue: "#A4B4FF",
magenta: "#FF79C6", magenta: "#FF4DA6",
cyan: "#8BE9FD", cyan: "#C9A0FF",
white: "#F8F8F2", white: "#C4B8D4",
), ),
bright: ( bright: (
black: "#6C7086", black: "#5A3875",
red: "#FF79C6", red: "#FF7A96",
green: "#8BE9FD", green: "#B5E8C0",
yellow: "#E79CFE", yellow: "#F0D99A",
blue: "#E79CFE", blue: "#BCC8FF",
magenta: "#E79CFE", magenta: "#C9A0FF",
cyan: "#8BE9FD", cyan: "#DFC0FF",
white: "#F8F8F2", white: "#F0EAF8",
), ),
dim: ( dim: (
black: "#261537", black: "#1A0E27",
red: "#F38BA8", red: "#C93A5A",
green: "#A6E3A1", green: "#6EBE80",
yellow: "#F9E2AF", yellow: "#B89840",
blue: "#3D2454", blue: "#7A8ACC",
magenta: "#FF79C6", magenta: "#D4348A",
cyan: "#8BE9FD", cyan: "#8A5CB8",
white: "#6C7086", white: "#716686",
), ),
), ),
} }

View File

@@ -1,40 +1,40 @@
{ {
0: ( 0: (
name: "violet-chaton", name: "violet-chaton-light",
foreground: "#F8F8F2", foreground: "#261537",
background: "#261537", background: "#F3EDF8",
cursor: "#FF79C6", cursor: "#D4348A",
bright_foreground: "#F8F8F2", bright_foreground: "#261537",
dim_foreground: "#6C7086", dim_foreground: "#8A7BA0",
normal: ( normal: (
black: "#261537", black: "#F3EDF8",
red: "#F38BA8", red: "#C93A5A",
green: "#A6E3A1", green: "#3D9E68",
yellow: "#F9E2AF", yellow: "#B89840",
blue: "#3D2454", blue: "#5A6AD0",
magenta: "#FF79C6", magenta: "#D4348A",
cyan: "#8BE9FD", cyan: "#8A5CB8",
white: "#F8F8F2", white: "#3D2454",
), ),
bright: ( bright: (
black: "#6C7086", black: "#C4B8D4",
red: "#FF79C6", red: "#E04A6A",
green: "#8BE9FD", green: "#4DAE78",
yellow: "#E79CFE", yellow: "#C8A850",
blue: "#E79CFE", blue: "#6A7AE0",
magenta: "#E79CFE", magenta: "#8A5CB8",
cyan: "#8BE9FD", cyan: "#A070D0",
white: "#F8F8F2", white: "#261537",
), ),
dim: ( dim: (
black: "#261537", black: "#EBE4F2",
red: "#F38BA8", red: "#A02840",
green: "#A6E3A1", green: "#2D7E50",
yellow: "#F9E2AF", yellow: "#987830",
blue: "#3D2454", blue: "#4A5AB0",
magenta: "#FF79C6", magenta: "#B42870",
cyan: "#8BE9FD", cyan: "#704898",
white: "#6C7086", white: "#5A3875",
), ),
), ),
} }

View File

@@ -1 +1 @@
"JetBrains Mono NL" "Maple Mono NF"

View File

@@ -1,74 +1,74 @@
( (
base: ( base: (
red: 0.90588236, red: 0.788,
green: 0.6117647, green: 0.627,
blue: 0.99607843, blue: 1.0,
alpha: 1.0, alpha: 1.0,
), ),
hover: ( hover: (
red: 0.8103273, red: 0.708,
green: 0.558776, green: 0.564,
blue: 0.90052974, blue: 0.900,
alpha: 1.0, alpha: 1.0,
), ),
pressed: ( pressed: (
red: 0.51201665, red: 0.469,
green: 0.32542938, green: 0.355,
blue: 0.59074855, blue: 0.608,
alpha: 1.0, alpha: 1.0,
), ),
selected: ( selected: (
red: 0.8103273, red: 0.708,
green: 0.558776, green: 0.564,
blue: 0.90052974, blue: 0.900,
alpha: 1.0, alpha: 1.0,
), ),
selected_text: ( selected_text: (
red: 0.90588236, red: 0.788,
green: 0.6117647, green: 0.627,
blue: 0.99607843, blue: 1.0,
alpha: 1.0, alpha: 1.0,
), ),
focus: ( focus: (
red: 0.90588236, red: 0.788,
green: 0.6117647, green: 0.627,
blue: 0.99607843, blue: 1.0,
alpha: 1.0, alpha: 1.0,
), ),
divider: ( divider: (
red: 0.0, red: 0.102,
green: 0.0, green: 0.055,
blue: 0.000000000000000000000000000000000000000000055, blue: 0.153,
alpha: 1.0, alpha: 1.0,
), ),
on: ( on: (
red: 0.0, red: 0.102,
green: 0.0, green: 0.055,
blue: 0.000000000000000000000000000000000000000000055, blue: 0.153,
alpha: 1.0, alpha: 1.0,
), ),
disabled: ( disabled: (
red: 0.90588236, red: 0.788,
green: 0.6117647, green: 0.627,
blue: 0.99607843, blue: 1.0,
alpha: 1.0, alpha: 0.5,
), ),
on_disabled: ( on_disabled: (
red: 0.45294118, red: 0.445,
green: 0.30588236, green: 0.341,
blue: 0.49803922, blue: 0.577,
alpha: 1.0, alpha: 1.0,
), ),
border: ( border: (
red: 0.90588236, red: 0.788,
green: 0.6117647, green: 0.627,
blue: 0.99607843, blue: 1.0,
alpha: 1.0, alpha: 1.0,
), ),
disabled_border: ( disabled_border: (
red: 0.90588236, red: 0.788,
green: 0.6117647, green: 0.627,
blue: 0.99607843, blue: 1.0,
alpha: 0.5, alpha: 0.5,
), ),
) )

View File

@@ -1,100 +1,100 @@
( (
base: ( base: (
red: 0.20392156, red: 0.149,
green: 0.109803915, green: 0.082,
blue: 0.2901961, blue: 0.216,
alpha: 1.0, alpha: 1.0,
), ),
component: ( component: (
base: ( base: (
red: 0.28618598, red: 0.239,
green: 0.19189146, green: 0.141,
blue: 0.3797182, blue: 0.329,
alpha: 1.0, alpha: 1.0,
), ),
hover: ( hover: (
red: 0.35756737, red: 0.286,
green: 0.27269349, green: 0.192,
blue: 0.44174638, blue: 0.380,
alpha: 1.0, alpha: 1.0,
), ),
pressed: ( pressed: (
red: 0.4289488, red: 0.353,
green: 0.35349554, green: 0.220,
blue: 0.5037746, blue: 0.459,
alpha: 1.0, alpha: 1.0,
), ),
selected: ( selected: (
red: 0.35756737, red: 0.286,
green: 0.27269349, green: 0.192,
blue: 0.44174638, blue: 0.380,
alpha: 1.0, alpha: 1.0,
), ),
selected_text: ( selected_text: (
red: 0.90588236, red: 0.788,
green: 0.6117647, green: 0.627,
blue: 0.99607843, blue: 1.0,
alpha: 1.0, alpha: 1.0,
), ),
focus: ( focus: (
red: 0.90588236, red: 0.788,
green: 0.6117647, green: 0.627,
blue: 0.99607843, blue: 1.0,
alpha: 1.0, alpha: 1.0,
), ),
divider: ( divider: (
red: 0.8318802, red: 0.769,
green: 0.8318715, green: 0.722,
blue: 0.8090188, blue: 0.831,
alpha: 0.2, alpha: 0.2,
), ),
on: ( on: (
red: 0.8318802, red: 0.941,
green: 0.8318715, green: 0.918,
blue: 0.8090188, blue: 0.973,
alpha: 1.0, alpha: 1.0,
), ),
disabled: ( disabled: (
red: 0.28618598, red: 0.239,
green: 0.19189146, green: 0.141,
blue: 0.3797182, blue: 0.329,
alpha: 0.5, alpha: 0.5,
), ),
on_disabled: ( on_disabled: (
red: 0.8318802, red: 0.941,
green: 0.8318715, green: 0.918,
blue: 0.8090188, blue: 0.973,
alpha: 0.65, alpha: 0.65,
), ),
border: ( border: (
red: 0.7874787, red: 0.353,
green: 0.6994349, green: 0.220,
blue: 0.8914889, blue: 0.459,
alpha: 1.0, alpha: 1.0,
), ),
disabled_border: ( disabled_border: (
red: 0.7874787, red: 0.353,
green: 0.6994349, green: 0.220,
blue: 0.8914889, blue: 0.459,
alpha: 0.5, alpha: 0.5,
), ),
), ),
divider: ( divider: (
red: 0.36091125, red: 0.204,
green: 0.28561735, green: 0.110,
blue: 0.42521048, blue: 0.290,
alpha: 1.0, alpha: 1.0,
), ),
on: ( on: (
red: 0.9888701, red: 0.941,
green: 0.98887116, green: 0.918,
blue: 0.9652681, blue: 0.973,
alpha: 1.0, alpha: 1.0,
), ),
small_widget: ( small_widget: (
red: 0.21216959, red: 0.443,
green: 0.20068932, green: 0.400,
blue: 0.22159976, blue: 0.525,
alpha: 0.25, alpha: 0.25,
), ),
) )

View File

@@ -1,74 +1,74 @@
( (
base: ( base: (
red: 0.9529412, red: 0.949,
green: 0.54509807, green: 0.361,
blue: 0.65882355, blue: 0.478,
alpha: 1.0, alpha: 1.0,
), ),
hover: ( hover: (
red: 0.84797436, red: 0.854,
green: 0.5054427, green: 0.325,
blue: 0.6307258, blue: 0.430,
alpha: 1.0, alpha: 1.0,
), ),
pressed: ( pressed: (
red: 0.53554606, red: 0.526,
green: 0.29209605, green: 0.208,
blue: 0.42212114, blue: 0.347,
alpha: 1.0, alpha: 1.0,
), ),
selected: ( selected: (
red: 0.84797436, red: 0.854,
green: 0.5054427, green: 0.325,
blue: 0.6307258, blue: 0.430,
alpha: 1.0, alpha: 1.0,
), ),
selected_text: ( selected_text: (
red: 0.90588236, red: 0.949,
green: 0.6117647, green: 0.361,
blue: 0.99607843, blue: 0.478,
alpha: 1.0, alpha: 1.0,
), ),
focus: ( focus: (
red: 0.90588236, red: 0.949,
green: 0.6117647, green: 0.361,
blue: 0.99607843, blue: 0.478,
alpha: 1.0, alpha: 1.0,
), ),
divider: ( divider: (
red: 0.0, red: 0.102,
green: 0.0, green: 0.055,
blue: 0.000000000000000000000000000000000000000000055, blue: 0.153,
alpha: 1.0, alpha: 1.0,
), ),
on: ( on: (
red: 0.0, red: 0.102,
green: 0.0, green: 0.055,
blue: 0.000000000000000000000000000000000000000000055, blue: 0.153,
alpha: 1.0, alpha: 1.0,
), ),
disabled: ( disabled: (
red: 0.9529412, red: 0.949,
green: 0.54509807, green: 0.361,
blue: 0.65882355, blue: 0.478,
alpha: 1.0, alpha: 0.5,
), ),
on_disabled: ( on_disabled: (
red: 0.4764706, red: 0.526,
green: 0.27254903, green: 0.208,
blue: 0.32941177, blue: 0.316,
alpha: 1.0, alpha: 1.0,
), ),
border: ( border: (
red: 0.9529412, red: 0.949,
green: 0.54509807, green: 0.361,
blue: 0.65882355, blue: 0.478,
alpha: 1.0, alpha: 1.0,
), ),
disabled_border: ( disabled_border: (
red: 0.9529412, red: 0.949,
green: 0.54509807, green: 0.361,
blue: 0.65882355, blue: 0.478,
alpha: 0.5, alpha: 0.5,
), ),
) )

View File

@@ -1 +1 @@
"Violet-chaton" "Violet-Chaton Dark"

View File

@@ -1,100 +1,100 @@
( (
base: ( base: (
red: 0.21960787, red: 0.204,
green: 0.13725492, green: 0.110,
blue: 0.29411766, blue: 0.290,
alpha: 1.0, alpha: 1.0,
), ),
component: ( component: (
base: ( base: (
red: 0.28282174, red: 0.239,
green: 0.19933021, green: 0.141,
blue: 0.36183798, blue: 0.329,
alpha: 1.0, alpha: 1.0,
), ),
hover: ( hover: (
red: 0.35453954, red: 0.286,
green: 0.27938837, green: 0.192,
blue: 0.42565417, blue: 0.380,
alpha: 1.0, alpha: 1.0,
), ),
pressed: ( pressed: (
red: 0.4262574, red: 0.353,
green: 0.35944653, green: 0.220,
blue: 0.48947042, blue: 0.459,
alpha: 1.0, alpha: 1.0,
), ),
selected: ( selected: (
red: 0.35453954, red: 0.286,
green: 0.27938837, green: 0.192,
blue: 0.42565417, blue: 0.380,
alpha: 1.0, alpha: 1.0,
), ),
selected_text: ( selected_text: (
red: 0.90588236, red: 0.788,
green: 0.6117647, green: 0.627,
blue: 0.99607843, blue: 1.0,
alpha: 1.0, alpha: 1.0,
), ),
focus: ( focus: (
red: 0.90588236, red: 0.788,
green: 0.6117647, green: 0.627,
blue: 0.99607843, blue: 1.0,
alpha: 1.0, alpha: 1.0,
), ),
divider: ( divider: (
red: 0.8318802, red: 0.769,
green: 0.8318715, green: 0.722,
blue: 0.8090188, blue: 0.831,
alpha: 0.2, alpha: 0.2,
), ),
on: ( on: (
red: 0.8318802, red: 0.941,
green: 0.8318715, green: 0.918,
blue: 0.8090188, blue: 0.973,
alpha: 1.0, alpha: 1.0,
), ),
disabled: ( disabled: (
red: 0.28282174, red: 0.239,
green: 0.19933021, green: 0.141,
blue: 0.36183798, blue: 0.329,
alpha: 0.5, alpha: 0.5,
), ),
on_disabled: ( on_disabled: (
red: 0.8318802, red: 0.941,
green: 0.8318715, green: 0.918,
blue: 0.8090188, blue: 0.973,
alpha: 0.65, alpha: 0.65,
), ),
border: ( border: (
red: 0.7874787, red: 0.353,
green: 0.6994349, green: 0.220,
blue: 0.8914889, blue: 0.459,
alpha: 1.0, alpha: 1.0,
), ),
disabled_border: ( disabled_border: (
red: 0.7874787, red: 0.353,
green: 0.6994349, green: 0.220,
blue: 0.8914889, blue: 0.459,
alpha: 0.5, alpha: 0.5,
), ),
), ),
divider: ( divider: (
red: 0.32676017, red: 0.149,
green: 0.2608749, green: 0.082,
blue: 0.38187551, blue: 0.216,
alpha: 1.0, alpha: 1.0,
), ),
on: ( on: (
red: 0.75536937, red: 0.941,
green: 0.7553548, green: 0.918,
blue: 0.73290694, blue: 0.973,
alpha: 1.0, alpha: 1.0,
), ),
small_widget: ( small_widget: (
red: 0.23257035, red: 0.443,
green: 0.22102591, green: 0.400,
blue: 0.24228081, blue: 0.525,
alpha: 0.25, alpha: 0.25,
), ),
) )

View File

@@ -1,74 +1,74 @@
( (
base: ( base: (
red: 0.6509804, red: 0.604,
green: 0.8901961, green: 0.859,
blue: 0.6313726, blue: 0.659,
alpha: 1.0, alpha: 1.0,
), ),
hover: ( hover: (
red: 0.60640574, red: 0.544,
green: 0.7815211, green: 0.773,
blue: 0.608765, blue: 0.593,
alpha: 1.0, alpha: 1.0,
), ),
pressed: ( pressed: (
red: 0.38456568, red: 0.353,
green: 0.46464506, green: 0.457,
blue: 0.40839565, blue: 0.406,
alpha: 1.0, alpha: 1.0,
), ),
selected: ( selected: (
red: 0.60640574, red: 0.544,
green: 0.7815211, green: 0.773,
blue: 0.608765, blue: 0.593,
alpha: 1.0, alpha: 1.0,
), ),
selected_text: ( selected_text: (
red: 0.90588236, red: 0.604,
green: 0.6117647, green: 0.859,
blue: 0.99607843, blue: 0.659,
alpha: 1.0, alpha: 1.0,
), ),
focus: ( focus: (
red: 0.90588236, red: 0.604,
green: 0.6117647, green: 0.859,
blue: 0.99607843, blue: 0.659,
alpha: 1.0, alpha: 1.0,
), ),
divider: ( divider: (
red: 0.0, red: 0.102,
green: 0.0, green: 0.055,
blue: 0.000000000000000000000000000000000000000000055, blue: 0.153,
alpha: 1.0, alpha: 1.0,
), ),
on: ( on: (
red: 0.0, red: 0.102,
green: 0.0, green: 0.055,
blue: 0.000000000000000000000000000000000000000000055, blue: 0.153,
alpha: 1.0, alpha: 1.0,
), ),
disabled: ( disabled: (
red: 0.6509804, red: 0.604,
green: 0.8901961, green: 0.859,
blue: 0.6313726, blue: 0.659,
alpha: 1.0, alpha: 0.5,
), ),
on_disabled: ( on_disabled: (
red: 0.3254902, red: 0.353,
green: 0.44509804, green: 0.457,
blue: 0.3156863, blue: 0.406,
alpha: 1.0, alpha: 1.0,
), ),
border: ( border: (
red: 0.6509804, red: 0.604,
green: 0.8901961, green: 0.859,
blue: 0.6313726, blue: 0.659,
alpha: 1.0, alpha: 1.0,
), ),
disabled_border: ( disabled_border: (
red: 0.6509804, red: 0.604,
green: 0.8901961, green: 0.859,
blue: 0.6313726, blue: 0.659,
alpha: 0.5, alpha: 0.5,
), ),
) )

View File

@@ -1,74 +1,74 @@
( (
base: ( base: (
red: 0.9764706, red: 0.910,
green: 0.8862745, green: 0.784,
blue: 0.6862745, blue: 0.478,
alpha: 1.0, alpha: 1.0,
), ),
hover: ( hover: (
red: 0.8667979, red: 0.819,
green: 0.7783838, green: 0.706,
blue: 0.6526866, blue: 0.430,
alpha: 1.0, alpha: 1.0,
), ),
pressed: ( pressed: (
red: 0.54731077, red: 0.506,
green: 0.46268427, green: 0.420,
blue: 0.43584663, blue: 0.316,
alpha: 1.0, alpha: 1.0,
), ),
selected: ( selected: (
red: 0.8667979, red: 0.819,
green: 0.7783838, green: 0.706,
blue: 0.6526866, blue: 0.430,
alpha: 1.0, alpha: 1.0,
), ),
selected_text: ( selected_text: (
red: 0.90588236, red: 0.910,
green: 0.6117647, green: 0.784,
blue: 0.99607843, blue: 0.478,
alpha: 1.0, alpha: 1.0,
), ),
focus: ( focus: (
red: 0.90588236, red: 0.910,
green: 0.6117647, green: 0.784,
blue: 0.99607843, blue: 0.478,
alpha: 1.0, alpha: 1.0,
), ),
divider: ( divider: (
red: 0.0, red: 0.102,
green: 0.0, green: 0.055,
blue: 0.000000000000000000000000000000000000000000055, blue: 0.153,
alpha: 1.0, alpha: 1.0,
), ),
on: ( on: (
red: 0.0, red: 0.102,
green: 0.0, green: 0.055,
blue: 0.000000000000000000000000000000000000000000055, blue: 0.153,
alpha: 1.0, alpha: 1.0,
), ),
disabled: ( disabled: (
red: 0.9764706, red: 0.910,
green: 0.8862745, green: 0.784,
blue: 0.6862745, blue: 0.478,
alpha: 1.0, alpha: 0.5,
), ),
on_disabled: ( on_disabled: (
red: 0.4882353, red: 0.506,
green: 0.44313726, green: 0.420,
blue: 0.34313726, blue: 0.316,
alpha: 1.0, alpha: 1.0,
), ),
border: ( border: (
red: 0.9764706, red: 0.910,
green: 0.8862745, green: 0.784,
blue: 0.6862745, blue: 0.478,
alpha: 1.0, alpha: 1.0,
), ),
disabled_border: ( disabled_border: (
red: 0.9764706, red: 0.910,
green: 0.8862745, green: 0.784,
blue: 0.6862745, blue: 0.478,
alpha: 0.5, alpha: 0.5,
), ),
) )

View File

@@ -1,74 +1,74 @@
( (
base: ( base: (
red: 0.9058823, red: 0.541,
green: 0.6117647, green: 0.361,
blue: 0.99607843, blue: 0.722,
alpha: 1.0, alpha: 1.0,
), ),
hover: ( hover: (
red: 0.7988549, red: 0.461,
green: 0.5659659, green: 0.301,
blue: 0.8900906, blue: 0.642,
alpha: 1.0, alpha: 1.0,
), ),
pressed: ( pressed: (
red: 0.81436175, red: 0.381,
green: 0.6745583, green: 0.241,
blue: 0.91372323, blue: 0.562,
alpha: 1.0, alpha: 1.0,
), ),
selected: ( selected: (
red: 0.7988549, red: 0.461,
green: 0.5659659, green: 0.301,
blue: 0.8900906, blue: 0.642,
alpha: 1.0, alpha: 1.0,
), ),
selected_text: ( selected_text: (
red: 0.9058823, red: 0.541,
green: 0.6117647, green: 0.361,
blue: 0.99607843, blue: 0.722,
alpha: 1.0, alpha: 1.0,
), ),
focus: ( focus: (
red: 0.9058823, red: 0.541,
green: 0.6117647, green: 0.361,
blue: 0.99607843, blue: 0.722,
alpha: 1.0, alpha: 1.0,
), ),
divider: ( divider: (
red: 0.9999203, red: 0.953,
green: 0.99998146, green: 0.929,
blue: 1.0, blue: 0.973,
alpha: 1.0, alpha: 1.0,
), ),
on: ( on: (
red: 0.9999203, red: 0.953,
green: 0.99998146, green: 0.929,
blue: 1.0, blue: 0.973,
alpha: 1.0, alpha: 1.0,
), ),
disabled: ( disabled: (
red: 0.9058823, red: 0.541,
green: 0.6117647, green: 0.361,
blue: 0.99607843, blue: 0.722,
alpha: 1.0, alpha: 1.0,
), ),
on_disabled: ( on_disabled: (
red: 0.9529013, red: 0.271,
green: 0.8058731, green: 0.181,
blue: 0.99803925, blue: 0.361,
alpha: 1.0, alpha: 1.0,
), ),
border: ( border: (
red: 0.9058823, red: 0.541,
green: 0.6117647, green: 0.361,
blue: 0.99607843, blue: 0.722,
alpha: 1.0, alpha: 1.0,
), ),
disabled_border: ( disabled_border: (
red: 0.9058823, red: 0.541,
green: 0.6117647, green: 0.361,
blue: 0.99607843, blue: 0.722,
alpha: 0.5, alpha: 0.5,
), ),
) )

View File

@@ -1,100 +1,100 @@
( (
base: ( base: (
red: 0.20392156, red: 0.953,
green: 0.109803915, green: 0.929,
blue: 0.2901961, blue: 0.973,
alpha: 1.0, alpha: 1.0,
), ),
component: ( component: (
base: ( base: (
red: 0.28618598, red: 0.867,
green: 0.19189146, green: 0.831,
blue: 0.3797182, blue: 0.910,
alpha: 1.0, alpha: 1.0,
), ),
hover: ( hover: (
red: 0.25756738, red: 0.816,
green: 0.17270231, green: 0.776,
blue: 0.3417464, blue: 0.871,
alpha: 1.0, alpha: 1.0,
), ),
pressed: ( pressed: (
red: 0.22894879, red: 0.769,
green: 0.15351318, green: 0.722,
blue: 0.30377457, blue: 0.831,
alpha: 1.0, alpha: 1.0,
), ),
selected: ( selected: (
red: 0.25756738, red: 0.816,
green: 0.17270231, green: 0.776,
blue: 0.3417464, blue: 0.871,
alpha: 1.0, alpha: 1.0,
), ),
selected_text: ( selected_text: (
red: 0.9058823, red: 0.541,
green: 0.6117647, green: 0.361,
blue: 0.99607843, blue: 0.722,
alpha: 1.0, alpha: 1.0,
), ),
focus: ( focus: (
red: 0.9058823, red: 0.541,
green: 0.6117647, green: 0.361,
blue: 0.99607843, blue: 0.722,
alpha: 1.0, alpha: 1.0,
), ),
divider: ( divider: (
red: 0.8318751, red: 0.149,
green: 0.8318648, green: 0.082,
blue: 0.80908096, blue: 0.216,
alpha: 0.2, alpha: 0.2,
), ),
on: ( on: (
red: 0.8318751, red: 0.149,
green: 0.8318648, green: 0.082,
blue: 0.80908096, blue: 0.216,
alpha: 1.0, alpha: 1.0,
), ),
disabled: ( disabled: (
red: 0.28618598, red: 0.867,
green: 0.19189146, green: 0.831,
blue: 0.3797182, blue: 0.910,
alpha: 0.5, alpha: 0.5,
), ),
on_disabled: ( on_disabled: (
red: 0.8318751, red: 0.149,
green: 0.8318648, green: 0.082,
blue: 0.80908096, blue: 0.216,
alpha: 0.65, alpha: 0.65,
), ),
border: ( border: (
red: 0.072988935, red: 0.541,
green: 0.079405405, green: 0.482,
blue: 0.14595589, blue: 0.627,
alpha: 1.0, alpha: 1.0,
), ),
disabled_border: ( disabled_border: (
red: 0.072988935, red: 0.541,
green: 0.079405405, green: 0.482,
blue: 0.14595589, blue: 0.627,
alpha: 0.5, alpha: 0.5,
), ),
), ),
divider: ( divider: (
red: 0.3609102, red: 0.769,
green: 0.285616, green: 0.722,
blue: 0.42522332, blue: 0.831,
alpha: 1.0, alpha: 1.0,
), ),
on: ( on: (
red: 0.98886484, red: 0.149,
green: 0.98886436, green: 0.082,
blue: 0.9653322, blue: 0.216,
alpha: 1.0, alpha: 1.0,
), ),
small_widget: ( small_widget: (
red: 0.20783836, red: 0.541,
green: 0.20930338, green: 0.482,
blue: 0.23179808, blue: 0.627,
alpha: 0.25, alpha: 0.25,
), ),
) )

View File

@@ -1,74 +1,74 @@
( (
base: ( base: (
red: 0.47058824, red: 0.788,
green: 0.16078432, green: 0.227,
blue: 0.18039216, blue: 0.353,
alpha: 1.0, alpha: 1.0,
), ),
hover: ( hover: (
red: 0.45061958, red: 0.698,
green: 0.20518154, green: 0.187,
blue: 0.23754153, blue: 0.303,
alpha: 1.0, alpha: 1.0,
), ),
pressed: ( pressed: (
red: 0.59671474, red: 0.588,
green: 0.44906804, green: 0.147,
blue: 0.5058801, blue: 0.253,
alpha: 1.0, alpha: 1.0,
), ),
selected: ( selected: (
red: 0.45061958, red: 0.698,
green: 0.20518154, green: 0.187,
blue: 0.23754153, blue: 0.303,
alpha: 1.0, alpha: 1.0,
), ),
selected_text: ( selected_text: (
red: 0.9058823, red: 0.541,
green: 0.6117647, green: 0.361,
blue: 0.99607843, blue: 0.722,
alpha: 1.0, alpha: 1.0,
), ),
focus: ( focus: (
red: 0.9058823, red: 0.541,
green: 0.6117647, green: 0.361,
blue: 0.99607843, blue: 0.722,
alpha: 1.0, alpha: 1.0,
), ),
divider: ( divider: (
red: 0.9999203, red: 0.953,
green: 0.99998146, green: 0.929,
blue: 1.0, blue: 0.973,
alpha: 1.0, alpha: 1.0,
), ),
on: ( on: (
red: 0.9999203, red: 0.953,
green: 0.99998146, green: 0.929,
blue: 1.0, blue: 0.973,
alpha: 1.0, alpha: 1.0,
), ),
disabled: ( disabled: (
red: 0.47058824, red: 0.788,
green: 0.16078432, green: 0.227,
blue: 0.18039216, blue: 0.353,
alpha: 1.0, alpha: 1.0,
), ),
on_disabled: ( on_disabled: (
red: 0.7352543, red: 0.394,
green: 0.5803829, green: 0.114,
blue: 0.5901961, blue: 0.177,
alpha: 1.0, alpha: 1.0,
), ),
border: ( border: (
red: 0.47058824, red: 0.788,
green: 0.16078432, green: 0.227,
blue: 0.18039216, blue: 0.353,
alpha: 1.0, alpha: 1.0,
), ),
disabled_border: ( disabled_border: (
red: 0.47058824, red: 0.788,
green: 0.16078432, green: 0.227,
blue: 0.18039216, blue: 0.353,
alpha: 0.5, alpha: 0.5,
), ),
) )

View File

@@ -1 +1 @@
"cosmic-light" "Violet-Chaton Light"

View File

@@ -1,100 +1,100 @@
( (
base: ( base: (
red: 0.21960787, red: 0.922,
green: 0.13725492, green: 0.894,
blue: 0.29411766, blue: 0.949,
alpha: 1.0, alpha: 1.0,
), ),
component: ( component: (
base: ( base: (
red: 0.28282174, red: 0.867,
green: 0.19933021, green: 0.831,
blue: 0.36183798, blue: 0.910,
alpha: 1.0, alpha: 1.0,
), ),
hover: ( hover: (
red: 0.25453955, red: 0.816,
green: 0.17939718, green: 0.776,
blue: 0.32565418, blue: 0.871,
alpha: 1.0, alpha: 1.0,
), ),
pressed: ( pressed: (
red: 0.2262574, red: 0.769,
green: 0.15946417, green: 0.722,
blue: 0.2894704, blue: 0.831,
alpha: 1.0, alpha: 1.0,
), ),
selected: ( selected: (
red: 0.25453955, red: 0.816,
green: 0.17939718, green: 0.776,
blue: 0.32565418, blue: 0.871,
alpha: 1.0, alpha: 1.0,
), ),
selected_text: ( selected_text: (
red: 0.9058823, red: 0.541,
green: 0.6117647, green: 0.361,
blue: 0.99607843, blue: 0.722,
alpha: 1.0, alpha: 1.0,
), ),
focus: ( focus: (
red: 0.9058823, red: 0.541,
green: 0.6117647, green: 0.361,
blue: 0.99607843, blue: 0.722,
alpha: 1.0, alpha: 1.0,
), ),
divider: ( divider: (
red: 0.8318751, red: 0.149,
green: 0.8318648, green: 0.082,
blue: 0.80908096, blue: 0.216,
alpha: 0.2, alpha: 0.2,
), ),
on: ( on: (
red: 0.8318751, red: 0.149,
green: 0.8318648, green: 0.082,
blue: 0.80908096, blue: 0.216,
alpha: 1.0, alpha: 1.0,
), ),
disabled: ( disabled: (
red: 0.28282174, red: 0.867,
green: 0.19933021, green: 0.831,
blue: 0.36183798, blue: 0.910,
alpha: 0.5, alpha: 0.5,
), ),
on_disabled: ( on_disabled: (
red: 0.8318751, red: 0.149,
green: 0.8318648, green: 0.082,
blue: 0.80908096, blue: 0.216,
alpha: 0.65, alpha: 0.65,
), ),
border: ( border: (
red: 0.072988935, red: 0.541,
green: 0.079405405, green: 0.482,
blue: 0.14595589, blue: 0.627,
alpha: 1.0, alpha: 1.0,
), ),
disabled_border: ( disabled_border: (
red: 0.072988935, red: 0.541,
green: 0.079405405, green: 0.482,
blue: 0.14595589, blue: 0.627,
alpha: 0.5, alpha: 0.5,
), ),
), ),
divider: ( divider: (
red: 0.32675916, red: 0.769,
green: 0.26087362, green: 0.722,
blue: 0.38188773, blue: 0.831,
alpha: 1.0, alpha: 1.0,
), ),
on: ( on: (
red: 0.7553642, red: 0.239,
green: 0.7553483, green: 0.141,
blue: 0.732968, blue: 0.329,
alpha: 1.0, alpha: 1.0,
), ),
small_widget: ( small_widget: (
red: 0.22808892, red: 0.541,
green: 0.22971416, green: 0.482,
blue: 0.25254303, blue: 0.627,
alpha: 0.25, alpha: 0.25,
), ),
) )

View File

@@ -1,74 +1,74 @@
( (
base: ( base: (
red: 0.09411765, red: 0.239,
green: 0.33333334, green: 0.620,
blue: 0.16078432, blue: 0.408,
alpha: 1.0, alpha: 1.0,
), ),
hover: ( hover: (
red: 0.14944312, red: 0.199,
green: 0.34322074, green: 0.540,
blue: 0.22185525, blue: 0.348,
alpha: 1.0, alpha: 1.0,
), ),
pressed: ( pressed: (
red: 0.40847942, red: 0.159,
green: 0.5353426, green: 0.440,
blue: 0.4960762, blue: 0.288,
alpha: 1.0, alpha: 1.0,
), ),
selected: ( selected: (
red: 0.14944312, red: 0.199,
green: 0.34322074, green: 0.540,
blue: 0.22185525, blue: 0.348,
alpha: 1.0, alpha: 1.0,
), ),
selected_text: ( selected_text: (
red: 0.9058823, red: 0.541,
green: 0.6117647, green: 0.361,
blue: 0.99607843, blue: 0.722,
alpha: 1.0, alpha: 1.0,
), ),
focus: ( focus: (
red: 0.9058823, red: 0.541,
green: 0.6117647, green: 0.361,
blue: 0.99607843, blue: 0.722,
alpha: 1.0, alpha: 1.0,
), ),
divider: ( divider: (
red: 0.9999203, red: 0.953,
green: 0.99998146, green: 0.929,
blue: 1.0, blue: 0.973,
alpha: 1.0, alpha: 1.0,
), ),
on: ( on: (
red: 0.9999203, red: 0.953,
green: 0.99998146, green: 0.929,
blue: 1.0, blue: 0.973,
alpha: 1.0, alpha: 1.0,
), ),
disabled: ( disabled: (
red: 0.09411765, red: 0.239,
green: 0.33333334, green: 0.620,
blue: 0.16078432, blue: 0.408,
alpha: 1.0, alpha: 1.0,
), ),
on_disabled: ( on_disabled: (
red: 0.547019, red: 0.120,
green: 0.6666574, green: 0.310,
blue: 0.5803922, blue: 0.204,
alpha: 1.0, alpha: 1.0,
), ),
border: ( border: (
red: 0.09411765, red: 0.239,
green: 0.33333334, green: 0.620,
blue: 0.16078432, blue: 0.408,
alpha: 1.0, alpha: 1.0,
), ),
disabled_border: ( disabled_border: (
red: 0.09411765, red: 0.239,
green: 0.33333334, green: 0.620,
blue: 0.16078432, blue: 0.408,
alpha: 0.5, alpha: 0.5,
), ),
) )

View File

@@ -1,74 +1,74 @@
( (
base: ( base: (
red: 0.3254902, red: 0.722,
green: 0.28235295, green: 0.596,
blue: 0.0, blue: 0.251,
alpha: 1.0, alpha: 1.0,
), ),
hover: ( hover: (
red: 0.33454114, red: 0.642,
green: 0.30243644, green: 0.526,
blue: 0.093227796, blue: 0.211,
alpha: 1.0, alpha: 1.0,
), ),
pressed: ( pressed: (
red: 0.5241657, red: 0.542,
green: 0.50985235, green: 0.436,
blue: 0.41568404, blue: 0.171,
alpha: 1.0, alpha: 1.0,
), ),
selected: ( selected: (
red: 0.33454114, red: 0.642,
green: 0.30243644, green: 0.526,
blue: 0.093227796, blue: 0.211,
alpha: 1.0, alpha: 1.0,
), ),
selected_text: ( selected_text: (
red: 0.9058823, red: 0.541,
green: 0.6117647, green: 0.361,
blue: 0.99607843, blue: 0.722,
alpha: 1.0, alpha: 1.0,
), ),
focus: ( focus: (
red: 0.9058823, red: 0.541,
green: 0.6117647, green: 0.361,
blue: 0.99607843, blue: 0.722,
alpha: 1.0, alpha: 1.0,
), ),
divider: ( divider: (
red: 0.9999203, red: 0.149,
green: 0.99998146, green: 0.082,
blue: 1.0, blue: 0.216,
alpha: 1.0, alpha: 1.0,
), ),
on: ( on: (
red: 0.9999203, red: 0.149,
green: 0.99998146, green: 0.082,
blue: 1.0, blue: 0.216,
alpha: 1.0, alpha: 1.0,
), ),
disabled: ( disabled: (
red: 0.3254902, red: 0.722,
green: 0.28235295, green: 0.596,
blue: 0.0, blue: 0.251,
alpha: 1.0, alpha: 1.0,
), ),
on_disabled: ( on_disabled: (
red: 0.66270524, red: 0.361,
green: 0.6411672, green: 0.298,
blue: 0.5, blue: 0.126,
alpha: 1.0, alpha: 1.0,
), ),
border: ( border: (
red: 0.3254902, red: 0.722,
green: 0.28235295, green: 0.596,
blue: 0.0, blue: 0.251,
alpha: 1.0, alpha: 1.0,
), ),
disabled_border: ( disabled_border: (
red: 0.3254902, red: 0.722,
green: 0.28235295, green: 0.596,
blue: 0.0, blue: 0.251,
alpha: 0.5, alpha: 0.5,
), ),
) )

137
INSTALL/themes/palette.sh Normal file
View File

@@ -0,0 +1,137 @@
#!/usr/bin/env bash
# ══════════════════════════════════════════════════════════════════════════════
# violet-chaton v2 — palette source de verite
#
# Inspiration : Mitsuri Kanroji — gradient rose → vert pastel
# Identite : univers violet profond, accents chauds/frais, zero emprunt
#
# Usage :
# source palette.sh
# echo "$VC_MAGENTA" → #ff4da6
# echo "${VC_RGB_MAGENTA}" → 255;77;166
# echo "${VC_ANSI_MAGENTA}" → \033[38;2;255;77;166m
# ══════════════════════════════════════════════════════════════════════════════
# ── Fond (signature violet-chaton) ───────────────────────────────────────────
VC_CRUST="#1a0e27" # le plus profond — borders, shadows
VC_BASE="#261537" # fond principal
VC_MANTLE="#341c4a" # fond secondaire
VC_SURFACE0="#3d2454" # elements poses
VC_SURFACE1="#493161" # elements hover
VC_SURFACE2="#5a3875" # selection, highlight
# ── Texte (teinte violet — pas du blanc pur) ─────────────────────────────────
VC_TEXT="#f0eaf8" # texte principal
VC_SUBTEXT1="#c4b8d4" # texte secondaire
VC_SUBTEXT0="#9a8fad" # labels, placeholders
VC_MUTED="#716686" # desactive, commentaires
# ── Accents ──────────────────────────────────────────────────────────────────
VC_MAGENTA="#ff4da6" # accent primaire — chaud, vif
VC_LILAC="#c9a0ff" # accent secondaire — doux, aerien
VC_MITSURI="#9adba8" # vert pastel — frais, unique
VC_LAVANDE="#a4b4ff" # bleu-violet — info, fonctions
VC_CHAMPAGNE="#e8c87a" # or chaud — casse le bicolore
# ── Semantiques (derivees des accents) ───────────────────────────────────────
VC_DANGER="#f25c7a" # rouge vif, teinte violet
VC_WARNING="#e8c87a" # = champagne
VC_SUCCESS="#9adba8" # = mitsuri green
VC_INFO="#a4b4ff" # = lavande
# ── Gradient signature (cava, barres, transitions) ───────────────────────────
# magenta → lilac → lavande → mitsuri
VC_GRADIENT_1="$VC_MAGENTA"
VC_GRADIENT_2="#e276d4" # magenta → lilac mid
VC_GRADIENT_3="$VC_LILAC"
VC_GRADIENT_4="#b6a8ff" # lilac → lavande mid
VC_GRADIENT_5="$VC_LAVANDE"
VC_GRADIENT_6="#9fc8d4" # lavande → mitsuri mid
VC_GRADIENT_7="#9adba8" # = mitsuri
VC_GRADIENT_8="#82e8a0" # mitsuri bright tip
# ══════════════════════════════════════════════════════════════════════════════
# LIGHT — meme univers violet, fonds lavande clairs
# Les accents sont legerement assombris pour le contraste sur fond clair
# ══════════════════════════════════════════════════════════════════════════════
# ── Fond light (lavande — identite violet conservee) ────────────────────────
VCL_CRUST="#f8f4fc" # le plus clair — presque blanc violet
VCL_BASE="#f3edf8" # fond principal
VCL_MANTLE="#ebe4f2" # fond secondaire
VCL_SURFACE0="#ddd4e8" # elements poses
VCL_SURFACE1="#d0c6de" # elements hover
VCL_SURFACE2="#c4b8d4" # selection, highlight
# ── Texte light (violet fonce — inversion du dark) ──────────────────────────
VCL_TEXT="#261537" # texte principal (= base dark)
VCL_SUBTEXT1="#3d2454" # texte secondaire
VCL_SUBTEXT0="#5a3875" # labels
VCL_MUTED="#8a7ba0" # desactive, commentaires
# ── Accents light (assombris pour contraste WCAG AA sur fond clair) ─────────
VCL_MAGENTA="#d4348a" # accent primaire
VCL_LILAC="#8a5cb8" # accent secondaire
VCL_MITSURI="#3d9e68" # vert mitsuri assombri
VCL_LAVANDE="#5a6ad0" # bleu-violet assombri
VCL_CHAMPAGNE="#b89840" # or chaud assombri
# ── Semantiques light ──────────────────────────────────────────────────────
VCL_DANGER="#c93a5a"
VCL_WARNING="#b89840"
VCL_SUCCESS="#3d9e68"
VCL_INFO="#5a6ad0"
# ── Font ─────────────────────────────────────────────────────────────────────
VC_FONT="Maple Mono NF"
VC_FONT_FALLBACK="MapleMono Nerd Font"
VC_FONT_SIZE=13
# ── RGB (pour les contextes qui n'acceptent pas le hex) ──────────────────────
_hex2rgb() { printf '%d;%d;%d' "0x${1:1:2}" "0x${1:3:2}" "0x${1:5:2}"; }
VC_RGB_CRUST=$(_hex2rgb "$VC_CRUST")
VC_RGB_BASE=$(_hex2rgb "$VC_BASE")
VC_RGB_MANTLE=$(_hex2rgb "$VC_MANTLE")
VC_RGB_SURFACE0=$(_hex2rgb "$VC_SURFACE0")
VC_RGB_SURFACE1=$(_hex2rgb "$VC_SURFACE1")
VC_RGB_SURFACE2=$(_hex2rgb "$VC_SURFACE2")
VC_RGB_TEXT=$(_hex2rgb "$VC_TEXT")
VC_RGB_SUBTEXT1=$(_hex2rgb "$VC_SUBTEXT1")
VC_RGB_SUBTEXT0=$(_hex2rgb "$VC_SUBTEXT0")
VC_RGB_MUTED=$(_hex2rgb "$VC_MUTED")
VC_RGB_MAGENTA=$(_hex2rgb "$VC_MAGENTA")
VC_RGB_LILAC=$(_hex2rgb "$VC_LILAC")
VC_RGB_MITSURI=$(_hex2rgb "$VC_MITSURI")
VC_RGB_LAVANDE=$(_hex2rgb "$VC_LAVANDE")
VC_RGB_CHAMPAGNE=$(_hex2rgb "$VC_CHAMPAGNE")
VC_RGB_DANGER=$(_hex2rgb "$VC_DANGER")
VC_RGB_WARNING=$(_hex2rgb "$VC_WARNING")
VC_RGB_SUCCESS=$(_hex2rgb "$VC_SUCCESS")
VC_RGB_INFO=$(_hex2rgb "$VC_INFO")
# ── ANSI (pour echo -e dans les scripts) ─────────────────────────────────────
_hex2ansi() { echo "\033[38;2;$(_hex2rgb "$1")m"; }
VC_ANSI_CRUST=$(_hex2ansi "$VC_CRUST")
VC_ANSI_BASE=$(_hex2ansi "$VC_BASE")
VC_ANSI_MANTLE=$(_hex2ansi "$VC_MANTLE")
VC_ANSI_SURFACE0=$(_hex2ansi "$VC_SURFACE0")
VC_ANSI_SURFACE1=$(_hex2ansi "$VC_SURFACE1")
VC_ANSI_SURFACE2=$(_hex2ansi "$VC_SURFACE2")
VC_ANSI_TEXT=$(_hex2ansi "$VC_TEXT")
VC_ANSI_SUBTEXT1=$(_hex2ansi "$VC_SUBTEXT1")
VC_ANSI_SUBTEXT0=$(_hex2ansi "$VC_SUBTEXT0")
VC_ANSI_MUTED=$(_hex2ansi "$VC_MUTED")
VC_ANSI_MAGENTA=$(_hex2ansi "$VC_MAGENTA")
VC_ANSI_LILAC=$(_hex2ansi "$VC_LILAC")
VC_ANSI_MITSURI=$(_hex2ansi "$VC_MITSURI")
VC_ANSI_LAVANDE=$(_hex2ansi "$VC_LAVANDE")
VC_ANSI_CHAMPAGNE=$(_hex2ansi "$VC_CHAMPAGNE")
VC_ANSI_DANGER=$(_hex2ansi "$VC_DANGER")
VC_ANSI_WARNING=$(_hex2ansi "$VC_WARNING")
VC_ANSI_SUCCESS=$(_hex2ansi "$VC_SUCCESS")
VC_ANSI_INFO=$(_hex2ansi "$VC_INFO")
VC_ANSI_RESET='\033[0m'
VC_ANSI_BOLD='\033[1m'
VC_ANSI_ITALIC='\033[3m'

View File

@@ -1,10 +1,10 @@
# violet-chaton — thème atuin # violet-chaton v2 — theme atuin
# Les valeurs sont des noms de couleurs CSS (palette crate) # Les valeurs sont des noms de couleurs CSS (palette crate)
Base = "white" # texte principal #f8f8f2 Base = "lavender_blush" # texte principal #f0eaf8
Important = "hot_pink" # éléments importants #ff79c6 Important = "hot_pink" # accent primaire #ff4da6
Guidance = "slate_gray" # texte d'aide/muted #7f849c Guidance = "medium_purple" # texte aide/muted #716686
Annotation = "orchid" # labels/annotations #e79cfe Annotation = "plum" # labels/annotations #c9a0ff
AlertInfo = "light_cyan" # info #8be9fd AlertInfo = "light_steel_blue" # info #a4b4ff
AlertWarn = "pale_goldenrod" # avertissement #f9e2af AlertWarn = "goldenrod" # warning #e8c87a
AlertError = "light_pink" # erreur #f38ba8 AlertError = "pale_violet_red" # danger #f25c7a

View File

@@ -11,12 +11,12 @@
<dict> <dict>
<key>settings</key> <key>settings</key>
<dict> <dict>
<key>background</key> <string>#341c4a</string> <key>background</key> <string>#261537</string>
<key>foreground</key> <string>#f8f8f2</string> <key>foreground</key> <string>#f0eaf8</string>
<key>caret</key> <string>#ff79c6</string> <key>caret</key> <string>#ff4da6</string>
<key>lineHighlight</key><string>#3d2454</string> <key>lineHighlight</key><string>#3d2454</string>
<key>selection</key> <string>#3d245480</string> <key>selection</key> <string>#5a387580</string>
<key>invisibles</key> <string>#6c7086</string> <key>invisibles</key> <string>#716686</string>
</dict> </dict>
</dict> </dict>
@@ -26,17 +26,17 @@
<key>scope</key><string>comment, punctuation.definition.comment</string> <key>scope</key><string>comment, punctuation.definition.comment</string>
<key>settings</key> <key>settings</key>
<dict> <dict>
<key>foreground</key><string>#7f849c</string> <key>foreground</key><string>#9a8fad</string>
<key>fontStyle</key><string>italic</string> <key>fontStyle</key><string>italic</string>
</dict> </dict>
</dict> </dict>
<!-- Chaînes --> <!-- Chaines -->
<dict> <dict>
<key>name</key><string>String</string> <key>name</key><string>String</string>
<key>scope</key><string>string, string.quoted, string.template</string> <key>scope</key><string>string, string.quoted, string.template</string>
<key>settings</key> <key>settings</key>
<dict><key>foreground</key><string>#e79cfe</string></dict> <dict><key>foreground</key><string>#9adba8</string></dict>
</dict> </dict>
<!-- Nombres --> <!-- Nombres -->
@@ -44,23 +44,23 @@
<key>name</key><string>Number</string> <key>name</key><string>Number</string>
<key>scope</key><string>constant.numeric</string> <key>scope</key><string>constant.numeric</string>
<key>settings</key> <key>settings</key>
<dict><key>foreground</key><string>#a6e3a1</string></dict> <dict><key>foreground</key><string>#e8c87a</string></dict>
</dict> </dict>
<!-- Mots-clés --> <!-- Mots-cles -->
<dict> <dict>
<key>name</key><string>Keyword</string> <key>name</key><string>Keyword</string>
<key>scope</key><string>keyword, keyword.control, storage, storage.type, storage.modifier</string> <key>scope</key><string>keyword, keyword.control, storage, storage.type, storage.modifier</string>
<key>settings</key> <key>settings</key>
<dict><key>foreground</key><string>#ff79c6</string></dict> <dict><key>foreground</key><string>#ff4da6</string></dict>
</dict> </dict>
<!-- Opérateurs --> <!-- Operateurs -->
<dict> <dict>
<key>name</key><string>Operator</string> <key>name</key><string>Operator</string>
<key>scope</key><string>keyword.operator</string> <key>scope</key><string>keyword.operator</string>
<key>settings</key> <key>settings</key>
<dict><key>foreground</key><string>#ff79c6</string></dict> <dict><key>foreground</key><string>#ff4da6</string></dict>
</dict> </dict>
<!-- Fonctions --> <!-- Fonctions -->
@@ -68,7 +68,7 @@
<key>name</key><string>Function</string> <key>name</key><string>Function</string>
<key>scope</key><string>entity.name.function, meta.function-call, support.function</string> <key>scope</key><string>entity.name.function, meta.function-call, support.function</string>
<key>settings</key> <key>settings</key>
<dict><key>foreground</key><string>#8be9fd</string></dict> <dict><key>foreground</key><string>#a4b4ff</string></dict>
</dict> </dict>
<!-- Types / Classes --> <!-- Types / Classes -->
@@ -76,15 +76,15 @@
<key>name</key><string>Type</string> <key>name</key><string>Type</string>
<key>scope</key><string>entity.name.class, entity.name.type, support.class, support.type</string> <key>scope</key><string>entity.name.class, entity.name.type, support.class, support.type</string>
<key>settings</key> <key>settings</key>
<dict><key>foreground</key><string>#e79cfe</string></dict> <dict><key>foreground</key><string>#c9a0ff</string></dict>
</dict> </dict>
<!-- Constantes / Booléens --> <!-- Constantes / Booleens -->
<dict> <dict>
<key>name</key><string>Constant</string> <key>name</key><string>Constant</string>
<key>scope</key><string>constant, constant.language, variable.language</string> <key>scope</key><string>constant, constant.language, variable.language</string>
<key>settings</key> <key>settings</key>
<dict><key>foreground</key><string>#8be9fd</string></dict> <dict><key>foreground</key><string>#a4b4ff</string></dict>
</dict> </dict>
<!-- Variables --> <!-- Variables -->
@@ -92,7 +92,7 @@
<key>name</key><string>Variable</string> <key>name</key><string>Variable</string>
<key>scope</key><string>variable, variable.other</string> <key>scope</key><string>variable, variable.other</string>
<key>settings</key> <key>settings</key>
<dict><key>foreground</key><string>#f8f8f2</string></dict> <dict><key>foreground</key><string>#f0eaf8</string></dict>
</dict> </dict>
<!-- Tags HTML/XML --> <!-- Tags HTML/XML -->
@@ -100,7 +100,7 @@
<key>name</key><string>Tag</string> <key>name</key><string>Tag</string>
<key>scope</key><string>entity.name.tag</string> <key>scope</key><string>entity.name.tag</string>
<key>settings</key> <key>settings</key>
<dict><key>foreground</key><string>#ff79c6</string></dict> <dict><key>foreground</key><string>#ff4da6</string></dict>
</dict> </dict>
<!-- Attributs --> <!-- Attributs -->
@@ -108,7 +108,10 @@
<key>name</key><string>Attribute</string> <key>name</key><string>Attribute</string>
<key>scope</key><string>entity.other.attribute-name</string> <key>scope</key><string>entity.other.attribute-name</string>
<key>settings</key> <key>settings</key>
<dict><key>foreground</key><string>#8be9fd</string></dict> <dict>
<key>foreground</key><string>#a4b4ff</string>
<key>fontStyle</key><string>italic</string>
</dict>
</dict> </dict>
<!-- Ponctuation --> <!-- Ponctuation -->
@@ -116,15 +119,15 @@
<key>name</key><string>Punctuation</string> <key>name</key><string>Punctuation</string>
<key>scope</key><string>punctuation</string> <key>scope</key><string>punctuation</string>
<key>settings</key> <key>settings</key>
<dict><key>foreground</key><string>#9399b2</string></dict> <dict><key>foreground</key><string>#9a8fad</string></dict>
</dict> </dict>
<!-- CSS propriétés --> <!-- CSS proprietes -->
<dict> <dict>
<key>name</key><string>CSS Property</string> <key>name</key><string>CSS Property</string>
<key>scope</key><string>support.type.property-name.css, support.type.property-name.scss</string> <key>scope</key><string>support.type.property-name.css, support.type.property-name.scss</string>
<key>settings</key> <key>settings</key>
<dict><key>foreground</key><string>#8be9fd</string></dict> <dict><key>foreground</key><string>#a4b4ff</string></dict>
</dict> </dict>
<!-- CSS valeurs --> <!-- CSS valeurs -->
@@ -132,7 +135,7 @@
<key>name</key><string>CSS Value</string> <key>name</key><string>CSS Value</string>
<key>scope</key><string>support.constant.property-value.css</string> <key>scope</key><string>support.constant.property-value.css</string>
<key>settings</key> <key>settings</key>
<dict><key>foreground</key><string>#e79cfe</string></dict> <dict><key>foreground</key><string>#c9a0ff</string></dict>
</dict> </dict>
<!-- SCSS variables --> <!-- SCSS variables -->
@@ -140,7 +143,56 @@
<key>name</key><string>SCSS Variable</string> <key>name</key><string>SCSS Variable</string>
<key>scope</key><string>variable.scss, variable.sass</string> <key>scope</key><string>variable.scss, variable.sass</string>
<key>settings</key> <key>settings</key>
<dict><key>foreground</key><string>#8be9fd</string></dict> <dict><key>foreground</key><string>#a4b4ff</string></dict>
</dict>
<!-- Regex -->
<dict>
<key>name</key><string>Regex</string>
<key>scope</key><string>string.regexp</string>
<key>settings</key>
<dict><key>foreground</key><string>#e8c87a</string></dict>
</dict>
<!-- Markdown heading -->
<dict>
<key>name</key><string>Markdown Heading</string>
<key>scope</key><string>markup.heading, entity.name.section</string>
<key>settings</key>
<dict>
<key>foreground</key><string>#ff4da6</string>
<key>fontStyle</key><string>bold</string>
</dict>
</dict>
<!-- Markdown bold -->
<dict>
<key>name</key><string>Markdown Bold</string>
<key>scope</key><string>markup.bold</string>
<key>settings</key>
<dict><key>fontStyle</key><string>bold</string></dict>
</dict>
<!-- Markdown italic -->
<dict>
<key>name</key><string>Markdown Italic</string>
<key>scope</key><string>markup.italic</string>
<key>settings</key>
<dict>
<key>foreground</key><string>#c9a0ff</string>
<key>fontStyle</key><string>italic</string>
</dict>
</dict>
<!-- Markdown link -->
<dict>
<key>name</key><string>Markdown Link</string>
<key>scope</key><string>markup.underline.link</string>
<key>settings</key>
<dict>
<key>foreground</key><string>#a4b4ff</string>
<key>fontStyle</key><string>underline</string>
</dict>
</dict> </dict>
<!-- Invalide --> <!-- Invalide -->
@@ -149,7 +201,7 @@
<key>scope</key><string>invalid</string> <key>scope</key><string>invalid</string>
<key>settings</key> <key>settings</key>
<dict> <dict>
<key>foreground</key><string>#f38ba8</string> <key>foreground</key><string>#f25c7a</string>
<key>fontStyle</key><string>bold</string> <key>fontStyle</key><string>bold</string>
</dict> </dict>
</dict> </dict>

View File

@@ -1,75 +1,75 @@
# violet-chaton btop theme # violet-chaton v2 btop theme
# Palette : ~/Documents/config/violet-chaton-discord-theme/src/modules/_variables.scss # Palette : themes/palette.sh — zero emprunt
# Main background # Main background
theme[main_bg]="#261537" theme[main_bg]="#261537"
# Main text color # Main text color
theme[main_fg]="#f8f8f2" theme[main_fg]="#f0eaf8"
# Title color for boxes # Title color for boxes
theme[title]="#e79cfe" theme[title]="#c9a0ff"
# Highlight color for keyboard shortcuts # Highlight color for keyboard shortcuts
theme[hi_fg]="#ff79c6" theme[hi_fg]="#ff4da6"
# Background color of selected item in processes box # Background color of selected item in processes box
theme[selected_bg]="#3d2454" theme[selected_bg]="#5a3875"
# Foreground color of selected item in processes box # Foreground color of selected item in processes box
theme[selected_fg]="#f8f8f2" theme[selected_fg]="#f0eaf8"
# Color of inactive/disabled text # Color of inactive/disabled text
theme[inactive_fg]="#6c7086" theme[inactive_fg]="#716686"
# Misc colors for processes box (mini cpu graphs, details, status) # Misc colors for processes box (mini cpu graphs, details, status)
theme[proc_misc]="#8be9fd" theme[proc_misc]="#a4b4ff"
# Box outline colors # Box outline colors
theme[cpu_box]="#38234b" theme[cpu_box]="#3d2454"
theme[mem_box]="#38234b" theme[mem_box]="#3d2454"
theme[net_box]="#38234b" theme[net_box]="#3d2454"
theme[proc_box]="#38234b" theme[proc_box]="#3d2454"
# Box divider line # Box divider line
theme[div_line]="#341c4a" theme[div_line]="#341c4a"
# Temperature graph : cyan (froid) → warning (chaud) → danger (critique) # Temperature graph : lavande (froid) → champagne (chaud) → danger (critique)
theme[temp_start]="#8be9fd" theme[temp_start]="#a4b4ff"
theme[temp_mid]="#f9e2af" theme[temp_mid]="#e8c87a"
theme[temp_end]="#f38ba8" theme[temp_end]="#f25c7a"
# CPU graph : purple → pink → danger # CPU graph : lilac → magenta → danger
theme[cpu_start]="#e79cfe" theme[cpu_start]="#c9a0ff"
theme[cpu_mid]="#ff79c6" theme[cpu_mid]="#ff4da6"
theme[cpu_end]="#f38ba8" theme[cpu_end]="#f25c7a"
# Mem/Disk free meter : green → cyan → purple # Mem/Disk free meter : mitsuri → lavande → lilac
theme[free_start]="#a6e3a1" theme[free_start]="#9adba8"
theme[free_mid]="#8be9fd" theme[free_mid]="#a4b4ff"
theme[free_end]="#e79cfe" theme[free_end]="#c9a0ff"
# Mem/Disk cached meter : purple subtil # Mem/Disk cached meter : surface subtil → lilac
theme[cached_start]="#3d2454" theme[cached_start]="#3d2454"
theme[cached_mid]="#e79cfe" theme[cached_mid]="#c9a0ff"
theme[cached_end]="#e79cfe" theme[cached_end]="#c9a0ff"
# Mem/Disk available meter : cyan → purple # Mem/Disk available meter : lavande → lilac → magenta
theme[available_start]="#8be9fd" theme[available_start]="#a4b4ff"
theme[available_mid]="#e79cfe" theme[available_mid]="#c9a0ff"
theme[available_end]="#ff79c6" theme[available_end]="#ff4da6"
# Mem/Disk used meter : purple → pink → danger # Mem/Disk used meter : lilac → magenta → danger
theme[used_start]="#e79cfe" theme[used_start]="#c9a0ff"
theme[used_mid]="#ff79c6" theme[used_mid]="#ff4da6"
theme[used_end]="#f38ba8" theme[used_end]="#f25c7a"
# Download graph : cyan → purple # Download graph : lavande → lilac → magenta
theme[download_start]="#8be9fd" theme[download_start]="#a4b4ff"
theme[download_mid]="#e79cfe" theme[download_mid]="#c9a0ff"
theme[download_end]="#ff79c6" theme[download_end]="#ff4da6"
# Upload graph : pink → danger # Upload graph : magenta → danger → champagne
theme[upload_start]="#ff79c6" theme[upload_start]="#ff4da6"
theme[upload_mid]="#f38ba8" theme[upload_mid]="#f25c7a"
theme[upload_end]="#f9e2af" theme[upload_end]="#e8c87a"

View File

@@ -17,16 +17,16 @@ mono_option = average
reverse = 0 reverse = 0
[color] [color]
# background commenté = transparent # background commente = transparent
gradient = 1 gradient = 1
gradient_color_1 = '#ff79c6' gradient_color_1 = '#ff4da6'
gradient_color_2 = '#f079d8' gradient_color_2 = '#e276d4'
gradient_color_3 = '#e079ea' gradient_color_3 = '#c9a0ff'
gradient_color_4 = '#e79cfe' gradient_color_4 = '#b6a8ff'
gradient_color_5 = '#c0b0fe' gradient_color_5 = '#a4b4ff'
gradient_color_6 = '#99c4fe' gradient_color_6 = '#9fc8d4'
gradient_color_7 = '#8bd4fe' gradient_color_7 = '#9adba8'
gradient_color_8 = '#8be9fd' gradient_color_8 = '#82e8a0'
[smoothing] [smoothing]
monstercat = 1 monstercat = 1

View File

@@ -0,0 +1,128 @@
/* ── violet-chaton v2 GTK Light theme (adw-gtk3 compatible) ──────────────────
* Palette : themes/palette.sh — section LIGHT
* Meme univers violet, fonds lavande clairs
* ─────────────────────────────────────────────────────────────────────────── */
@define-color window_bg_color rgba(243, 237, 248, 1.00);
@define-color window_fg_color rgba(38, 21, 55, 1.00);
@define-color view_bg_color rgba(248, 244, 252, 1.00);
@define-color view_fg_color rgba(61, 36, 84, 1.00);
@define-color headerbar_bg_color rgba(235, 228, 242, 1.00);
@define-color headerbar_fg_color rgba(38, 21, 55, 1.00);
@define-color headerbar_border_color_color rgba(196, 184, 212, 1.00);
@define-color headerbar_backdrop_color rgba(243, 237, 248, 1.00);
@define-color sidebar_bg_color rgba(235, 228, 242, 1.00);
@define-color sidebar_fg_color rgba(61, 36, 84, 1.00);
@define-color sidebar_shade_color rgba(0, 0, 0, 0.06);
@define-color sidebar_backdrop_color rgba(221, 212, 232, 1.00);
@define-color secondary_sidebar_bg_color rgba(221, 212, 232, 1.00);
@define-color secondary_sidebar_fg_color rgba(61, 36, 84, 1.00);
@define-color secondary_sidebar_shade_color rgba(0, 0, 0, 0.06);
@define-color secondary_sidebar_backdrop_color rgba(208, 198, 222, 1.00);
@define-color card_bg_color rgba(248, 244, 252, 1.00);
@define-color card_fg_color rgba(61, 36, 84, 1.00);
@define-color thumbnail_bg_color rgba(248, 244, 252, 1.00);
@define-color thumbnail_fg_color rgba(61, 36, 84, 1.00);
@define-color dialog_bg_color rgba(243, 237, 248, 1.00);
@define-color dialog_fg_color rgba(38, 21, 55, 1.00);
@define-color popover_bg_color rgba(248, 244, 252, 1.00);
@define-color popover_fg_color rgba(61, 36, 84, 1.00);
@define-color shade_color rgba(0, 0, 0, 0.12);
@define-color scrollbar_outline_color rgba(196, 184, 212, 0.50);
/* Accent = lilac assombri */
@define-color accent_color rgba(138, 92, 184, 1.00);
@define-color accent_bg_color rgba(138, 92, 184, 1.00);
@define-color accent_fg_color rgba(248, 244, 252, 1.00);
/* Destructive = danger */
@define-color destructive_color rgba(201, 58, 90, 1.00);
@define-color destructive_bg_color rgba(201, 58, 90, 1.00);
@define-color destructive_fg_color rgba(248, 244, 252, 1.00);
/* Warning = champagne */
@define-color warning_color rgba(184, 152, 64, 1.00);
@define-color warning_bg_color rgba(184, 152, 64, 1.00);
@define-color warning_fg_color rgba(38, 21, 55, 1.00);
/* Success = mitsuri */
@define-color success_color rgba(61, 158, 104, 1.00);
@define-color success_bg_color rgba(61, 158, 104, 1.00);
@define-color success_fg_color rgba(248, 244, 252, 1.00);
/* Error = danger */
@define-color error_color rgba(201, 58, 90, 1.00);
@define-color error_bg_color rgba(201, 58, 90, 1.00);
@define-color error_fg_color rgba(248, 244, 252, 1.00);
/* Bleu = lavande assombri */
@define-color blue_1 rgba(110, 130, 224, 1.00);
@define-color blue_2 rgba(100, 120, 216, 1.00);
@define-color blue_3 rgba(90, 106, 208, 1.00);
@define-color blue_4 rgba(72, 86, 180, 1.00);
@define-color blue_5 rgba(54, 66, 152, 1.00);
/* Vert = mitsuri assombri */
@define-color green_1 rgba(80, 176, 124, 1.00);
@define-color green_2 rgba(70, 168, 114, 1.00);
@define-color green_3 rgba(61, 158, 104, 1.00);
@define-color green_4 rgba(48, 132, 84, 1.00);
@define-color green_5 rgba(36, 106, 66, 1.00);
/* Jaune = champagne assombri */
@define-color yellow_1 rgba(200, 170, 86, 1.00);
@define-color yellow_2 rgba(192, 162, 76, 1.00);
@define-color yellow_3 rgba(184, 152, 64, 1.00);
@define-color yellow_4 rgba(158, 128, 44, 1.00);
@define-color yellow_5 rgba(132, 104, 26, 1.00);
/* Rouge = danger assombri */
@define-color red_1 rgba(218, 82, 114, 1.00);
@define-color red_2 rgba(210, 70, 102, 1.00);
@define-color red_3 rgba(201, 58, 90, 1.00);
@define-color red_4 rgba(174, 42, 72, 1.00);
@define-color red_5 rgba(148, 28, 56, 1.00);
/* Orange */
@define-color orange_1 rgba(210, 154, 100, 1.00);
@define-color orange_2 rgba(202, 144, 88, 1.00);
@define-color orange_3 rgba(192, 132, 76, 1.00);
@define-color orange_4 rgba(164, 108, 56, 1.00);
@define-color orange_5 rgba(136, 84, 38, 1.00);
/* Purple = lilac → magenta assombris */
@define-color purple_1 rgba(156, 116, 200, 1.00);
@define-color purple_2 rgba(148, 104, 192, 1.00);
@define-color purple_3 rgba(138, 92, 184, 1.00);
@define-color purple_4 rgba(114, 72, 160, 1.00);
@define-color purple_5 rgba(92, 54, 136, 1.00);
/* Echelle monochrome lavande */
@define-color light_0 rgba(248, 244, 252, 1.00);
@define-color light_1 rgba(243, 237, 248, 1.00);
@define-color light_2 rgba(235, 228, 242, 1.00);
@define-color light_3 rgba(221, 212, 232, 1.00);
@define-color light_4 rgba(208, 198, 222, 1.00);
@define-color dark_0 rgba(196, 184, 212, 1.00);
@define-color dark_1 rgba(138, 123, 160, 1.00);
@define-color dark_2 rgba(90, 56, 117, 1.00);
@define-color dark_3 rgba(61, 36, 84, 1.00);
@define-color dark_4 rgba(38, 21, 55, 1.00);
/* ── Variables GTK3 classiques (compat apps legacy) ─────────────────────── */
@define-color theme_bg_color rgba(243, 237, 248, 1.00);
@define-color theme_fg_color rgba(38, 21, 55, 1.00);
@define-color theme_base_color rgba(248, 244, 252, 1.00);
@define-color theme_selected_bg_color rgba(138, 92, 184, 1.00);
@define-color theme_selected_fg_color rgba(248, 244, 252, 1.00);
@define-color theme_text_color rgba(38, 21, 55, 1.00);
@define-color borders rgba(196, 184, 212, 1.00);

View File

@@ -1,247 +1,127 @@
/* ── violet-chaton GTK3 theme — Nemo & GTK apps ─────────────────────────────── /* ── violet-chaton v2 GTK theme (adw-gtk3-dark compatible) ───────────────────
* * Palette : themes/palette.sh — source de verite unique
* 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)
* ─────────────────────────────────────────────────────────────────────────── */ * ─────────────────────────────────────────────────────────────────────────── */
@define-color theme_bg_color #341C4A; @define-color window_bg_color rgba(38, 21, 55, 1.00);
@define-color theme_fg_color #FCFCF6; @define-color window_fg_color rgba(240, 234, 248, 1.00);
@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;
/* ── Fenêtre principale ────────────────────────────────────────────────────── */ @define-color view_bg_color rgba(52, 28, 74, 1.00);
window, .background { @define-color view_fg_color rgba(196, 184, 212, 1.00);
background-color: #341C4A;
color: #FCFCF6;
}
/* ── Barre de titre / headerbar ───────────────────────────────────────────── */ @define-color headerbar_bg_color rgba(38, 21, 55, 1.00);
headerbar, .titlebar { @define-color headerbar_fg_color rgba(240, 234, 248, 1.00);
background-color: #493161; @define-color headerbar_border_color_color rgba(90, 56, 117, 1.00);
color: #FCFCF6; @define-color headerbar_backdrop_color rgba(38, 21, 55, 1.00);
border-bottom: 1px solid #5C496C;
}
headerbar button, .titlebar button { @define-color sidebar_bg_color rgba(52, 28, 74, 1.00);
background-color: transparent; @define-color sidebar_fg_color rgba(196, 184, 212, 1.00);
color: #FCFCF6; @define-color sidebar_shade_color rgba(0, 0, 0, 0.08);
border: none; @define-color sidebar_backdrop_color rgba(61, 36, 84, 1.00);
border-radius: 6px;
}
headerbar button:hover, .titlebar button:hover { @define-color secondary_sidebar_bg_color rgba(61, 36, 84, 1.00);
background-color: #5B4671; @define-color secondary_sidebar_fg_color rgba(196, 184, 212, 1.00);
} @define-color secondary_sidebar_shade_color rgba(0, 0, 0, 0.08);
@define-color secondary_sidebar_backdrop_color rgba(73, 49, 97, 1.00);
headerbar button:active, .titlebar button:active { @define-color card_bg_color rgba(61, 36, 84, 1.00);
background-color: #E79CFE; @define-color card_fg_color rgba(196, 184, 212, 1.00);
color: #341C4A;
}
/* ── Sidebar Nemo (paneau des emplacements) ───────────────────────────────── */ @define-color thumbnail_bg_color rgba(61, 36, 84, 1.00);
.sidebar, placessidebar { @define-color thumbnail_fg_color rgba(196, 184, 212, 1.00);
background-color: #2B1540;
color: #FCFCF6;
border-right: 1px solid #5C496C;
}
.sidebar row, placessidebar row { @define-color dialog_bg_color rgba(52, 28, 74, 1.00);
border-radius: 6px; @define-color dialog_fg_color rgba(196, 184, 212, 1.00);
padding: 2px 4px;
}
.sidebar row:hover, placessidebar row:hover { @define-color popover_bg_color rgba(61, 36, 84, 1.00);
background-color: #5B4671; @define-color popover_fg_color rgba(196, 184, 212, 1.00);
}
.sidebar row:selected, placessidebar row:selected { @define-color shade_color rgba(0, 0, 0, 0.32);
background-color: #E79CFE; @define-color scrollbar_outline_color rgba(38, 21, 55, 0.50);
color: #341C4A;
}
.sidebar .sidebar-section-header, placessidebar .sidebar-section-header { /* Accent = lilac (v2) */
color: #7F849C; @define-color accent_color rgba(201, 160, 255, 1.00);
font-size: smaller; @define-color accent_bg_color rgba(201, 160, 255, 1.00);
} @define-color accent_fg_color rgba(26, 14, 39, 1.00);
/* ── Vue fichiers (icônes + liste) ───────────────────────────────────────── */ /* Destructive = danger */
.view, iconview, treeview { @define-color destructive_color rgba(242, 92, 122, 1.00);
background-color: #341C4A; @define-color destructive_bg_color rgba(242, 92, 122, 1.00);
color: #FCFCF6; @define-color destructive_fg_color rgba(26, 14, 39, 1.00);
}
.view:selected, iconview:selected, /* Warning = champagne */
treeview:selected, .view:focus:selected { @define-color warning_color rgba(232, 200, 122, 1.00);
background-color: #E79CFE; @define-color warning_bg_color rgba(232, 200, 122, 1.00);
color: #341C4A; @define-color warning_fg_color rgba(26, 14, 39, 1.00);
}
/* En-têtes de colonnes (vue liste) */ /* Success = mitsuri */
treeview header button { @define-color success_color rgba(154, 219, 168, 1.00);
background-color: #493161; @define-color success_bg_color rgba(154, 219, 168, 1.00);
color: #FCFCF6; @define-color success_fg_color rgba(26, 14, 39, 1.00);
border: none;
border-right: 1px solid #5C496C;
border-bottom: 1px solid #5C496C;
}
treeview header button:hover { /* Error = danger */
background-color: #5B4671; @define-color error_color rgba(242, 92, 122, 1.00);
} @define-color error_bg_color rgba(242, 92, 122, 1.00);
@define-color error_fg_color rgba(26, 14, 39, 1.00);
/* ── Barre d'outils / pathbar ─────────────────────────────────────────────── */ /* Bleu = lavande */
toolbar, .path-bar { @define-color blue_1 rgba(180, 196, 255, 1.00);
background-color: #493161; @define-color blue_2 rgba(172, 188, 255, 1.00);
border-bottom: 1px solid #5C496C; @define-color blue_3 rgba(164, 180, 255, 1.00);
} @define-color blue_4 rgba(138, 154, 228, 1.00);
@define-color blue_5 rgba(112, 128, 200, 1.00);
.path-bar button { /* Vert = mitsuri */
background-color: transparent; @define-color green_1 rgba(170, 230, 182, 1.00);
color: #FCFCF6; @define-color green_2 rgba(162, 226, 175, 1.00);
border: none; @define-color green_3 rgba(154, 219, 168, 1.00);
border-radius: 6px; @define-color green_4 rgba(128, 192, 142, 1.00);
} @define-color green_5 rgba(102, 166, 116, 1.00);
.path-bar button:hover { /* Jaune = champagne */
background-color: #5B4671; @define-color yellow_1 rgba(242, 212, 140, 1.00);
} @define-color yellow_2 rgba(238, 206, 132, 1.00);
@define-color yellow_3 rgba(232, 200, 122, 1.00);
@define-color yellow_4 rgba(204, 174, 98, 1.00);
@define-color yellow_5 rgba(176, 148, 74, 1.00);
.path-bar button:checked { /* Rouge = danger */
background-color: #5B4671; @define-color red_1 rgba(254, 116, 146, 1.00);
color: #E79CFE; @define-color red_2 rgba(248, 104, 134, 1.00);
} @define-color red_3 rgba(242, 92, 122, 1.00);
@define-color red_4 rgba(216, 70, 100, 1.00);
@define-color red_5 rgba(190, 48, 78, 1.00);
/* ── Barre de recherche / entrée texte ────────────────────────────────────── */ /* Orange (derive du champagne chaud) */
entry { @define-color orange_1 rgba(248, 186, 140, 1.00);
background-color: #5B4671; @define-color orange_2 rgba(244, 178, 130, 1.00);
color: #FCFCF6; @define-color orange_3 rgba(238, 168, 118, 1.00);
border: 1px solid #5C496C; @define-color orange_4 rgba(210, 142, 94, 1.00);
border-radius: 6px; @define-color orange_5 rgba(182, 116, 70, 1.00);
padding: 4px 8px;
}
entry:focus { /* Purple = lilac → magenta */
border-color: #E79CFE; @define-color purple_1 rgba(216, 180, 255, 1.00);
box-shadow: 0 0 0 2px alpha(#E79CFE, 0.3); @define-color purple_2 rgba(208, 170, 255, 1.00);
} @define-color purple_3 rgba(201, 160, 255, 1.00);
@define-color purple_4 rgba(174, 136, 228, 1.00);
@define-color purple_5 rgba(148, 112, 200, 1.00);
entry placeholder { /* Echelle monochrome violet */
color: #7F849C; @define-color light_0 rgba(0, 0, 0, 1.00);
} @define-color light_1 rgba(10, 5, 16, 1.00);
@define-color light_2 rgba(26, 14, 39, 1.00);
@define-color light_3 rgba(52, 28, 74, 1.00);
@define-color light_4 rgba(73, 49, 97, 1.00);
@define-color dark_0 rgba(113, 102, 134, 1.00);
@define-color dark_1 rgba(154, 143, 173, 1.00);
@define-color dark_2 rgba(196, 184, 212, 1.00);
@define-color dark_3 rgba(240, 234, 248, 1.00);
@define-color dark_4 rgba(255, 255, 255, 1.00);
/* ── Scrollbar ────────────────────────────────────────────────────────────── */ /* ── Variables GTK3 classiques (compat apps legacy) ─────────────────────── */
scrollbar { @define-color theme_bg_color rgba(38, 21, 55, 1.00);
background-color: transparent; @define-color theme_fg_color rgba(240, 234, 248, 1.00);
} @define-color theme_base_color rgba(61, 36, 84, 1.00);
@define-color theme_selected_bg_color rgba(201, 160, 255, 1.00);
scrollbar slider { @define-color theme_selected_fg_color rgba(26, 14, 39, 1.00);
background-color: #5C496C; @define-color theme_text_color rgba(240, 234, 248, 1.00);
border-radius: 10px; @define-color borders rgba(90, 56, 117, 1.00);
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;
}

View File

@@ -1,23 +1,19 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Génère les LS_COLORS violet-chaton depuis catppuccin-mocha # ── violet-chaton v2 — LS_COLORS ────────────────────────────────────────────
# Genere les LS_COLORS depuis la palette v2 sans dependance a catppuccin
# Usage : export LS_COLORS="$(bash violet-chaton-ls-colors.sh)" # Usage : export LS_COLORS="$(bash violet-chaton-ls-colors.sh)"
#
# Palette v2 :
# magenta #ff4da6 executables
# lilac #c9a0ff dossiers
# mitsuri #9adba8 fichiers texte/code
# lavande #a4b4ff liens symboliques
# champagne #e8c87a archives/comprimes
# danger #f25c7a fichiers speciaux (devices, sockets)
# text #f0eaf8 fichiers normaux
# muted #716686 fichiers caches/backup
# subtext0 #9a8fad metadata
vivid generate catppuccin-mocha | \ cat << 'LSCOLORS'
sed \ no=0:fi=0:rs=0:di=1;38;2;201;160;255:ln=38;2;164;180;255:mh=0:pi=38;2;232;200;122;48;2;38;21;55:so=1;38;2;242;92;122:do=1;38;2;242;92;122:bd=38;2;232;200;122;48;2;61;36;84:cd=38;2;232;200;122;48;2;38;21;55:or=1;38;2;242;92;122;48;2;38;21;55:su=38;2;240;234;248;48;2;242;92;122:sg=38;2;240;234;248;48;2;232;200;122:ca=0:tw=38;2;240;234;248;48;2;61;158;104:ow=38;2;201;160;255;48;2;61;36;84:st=38;2;240;234;248;48;2;164;180;255:ex=1;38;2;255;77;166:mi=1;38;2;242;92;122;48;2;38;21;55:*.tar=38;2;232;200;122:*.tgz=38;2;232;200;122:*.arc=38;2;232;200;122:*.arj=38;2;232;200;122:*.taz=38;2;232;200;122:*.lha=38;2;232;200;122:*.lz4=38;2;232;200;122:*.lzh=38;2;232;200;122:*.lzma=38;2;232;200;122:*.tlz=38;2;232;200;122:*.txz=38;2;232;200;122:*.tzo=38;2;232;200;122:*.t7z=38;2;232;200;122:*.zip=38;2;232;200;122:*.z=38;2;232;200;122:*.dz=38;2;232;200;122:*.gz=38;2;232;200;122:*.lrz=38;2;232;200;122:*.lz=38;2;232;200;122:*.lzo=38;2;232;200;122:*.xz=38;2;232;200;122:*.zst=38;2;232;200;122:*.tzst=38;2;232;200;122:*.bz2=38;2;232;200;122:*.bz=38;2;232;200;122:*.tbz=38;2;232;200;122:*.tbz2=38;2;232;200;122:*.tz=38;2;232;200;122:*.deb=38;2;232;200;122:*.rpm=38;2;232;200;122:*.jar=38;2;232;200;122:*.war=38;2;232;200;122:*.ear=38;2;232;200;122:*.sar=38;2;232;200;122:*.rar=38;2;232;200;122:*.alz=38;2;232;200;122:*.ace=38;2;232;200;122:*.zoo=38;2;232;200;122:*.cpio=38;2;232;200;122:*.7z=38;2;232;200;122:*.rz=38;2;232;200;122:*.cab=38;2;232;200;122:*.wim=38;2;232;200;122:*.swm=38;2;232;200;122:*.dwm=38;2;232;200;122:*.esd=38;2;232;200;122:*.apk=38;2;232;200;122:*.jpg=38;2;255;77;166:*.jpeg=38;2;255;77;166:*.mjpg=38;2;255;77;166:*.mjpeg=38;2;255;77;166:*.gif=38;2;255;77;166:*.bmp=38;2;255;77;166:*.pbm=38;2;255;77;166:*.pgm=38;2;255;77;166:*.ppm=38;2;255;77;166:*.tga=38;2;255;77;166:*.xbm=38;2;255;77;166:*.xpm=38;2;255;77;166:*.tif=38;2;255;77;166:*.tiff=38;2;255;77;166:*.png=38;2;255;77;166:*.svg=38;2;255;77;166:*.svgz=38;2;255;77;166:*.mng=38;2;255;77;166:*.pcx=38;2;255;77;166:*.webp=38;2;255;77;166:*.avif=38;2;255;77;166:*.ico=38;2;255;77;166:*.mov=38;2;201;160;255:*.mpg=38;2;201;160;255:*.mpeg=38;2;201;160;255:*.m2v=38;2;201;160;255:*.mkv=38;2;201;160;255:*.webm=38;2;201;160;255:*.ogm=38;2;201;160;255:*.mp4=38;2;201;160;255:*.m4v=38;2;201;160;255:*.mp4v=38;2;201;160;255:*.vob=38;2;201;160;255:*.qt=38;2;201;160;255:*.nuv=38;2;201;160;255:*.wmv=38;2;201;160;255:*.asf=38;2;201;160;255:*.rm=38;2;201;160;255:*.rmvb=38;2;201;160;255:*.flc=38;2;201;160;255:*.avi=38;2;201;160;255:*.fli=38;2;201;160;255:*.flv=38;2;201;160;255:*.gl=38;2;201;160;255:*.dl=38;2;201;160;255:*.xcf=38;2;201;160;255:*.xwd=38;2;201;160;255:*.yuv=38;2;201;160;255:*.cgm=38;2;201;160;255:*.emf=38;2;201;160;255:*.ogv=38;2;201;160;255:*.aac=38;2;164;180;255:*.au=38;2;164;180;255:*.flac=38;2;164;180;255:*.m4a=38;2;164;180;255:*.mid=38;2;164;180;255:*.midi=38;2;164;180;255:*.mka=38;2;164;180;255:*.mp3=38;2;164;180;255:*.mpc=38;2;164;180;255:*.ogg=38;2;164;180;255:*.ra=38;2;164;180;255:*.wav=38;2;164;180;255:*.oga=38;2;164;180;255:*.opus=38;2;164;180;255:*.spx=38;2;164;180;255:*.xspf=38;2;164;180;255:*.pdf=38;2;242;92;122:*.doc=38;2;154;219;168:*.docx=38;2;154;219;168:*.xls=38;2;154;219;168:*.xlsx=38;2;154;219;168:*.ppt=38;2;154;219;168:*.pptx=38;2;154;219;168:*.odt=38;2;154;219;168:*.ods=38;2;154;219;168:*.odp=38;2;154;219;168:*.md=38;2;154;219;168:*.json=38;2;154;219;168:*.yaml=38;2;154;219;168:*.yml=38;2;154;219;168:*.toml=38;2;154;219;168:*.xml=38;2;154;219;168:*.csv=38;2;154;219;168:*.js=38;2;154;219;168:*.ts=38;2;154;219;168:*.py=38;2;154;219;168:*.rs=38;2;154;219;168:*.go=38;2;154;219;168:*.sh=38;2;154;219;168:*.bash=38;2;154;219;168:*.zsh=38;2;154;219;168:*.fish=38;2;154;219;168:*.c=38;2;154;219;168:*.cpp=38;2;154;219;168:*.h=38;2;154;219;168:*.hpp=38;2;154;219;168:*.java=38;2;154;219;168:*.css=38;2;154;219;168:*.scss=38;2;154;219;168:*.html=38;2;154;219;168:*.vue=38;2;154;219;168:*.svelte=38;2;154;219;168:*.tsx=38;2;154;219;168:*.jsx=38;2;154;219;168:*.sql=38;2;154;219;168:*.lua=38;2;154;219;168:*.rb=38;2;154;219;168:*.php=38;2;154;219;168:*.swift=38;2;154;219;168:*.kt=38;2;154;219;168:*.bak=38;2;113;102;134:*.old=38;2;113;102;134:*.orig=38;2;113;102;134:*.swp=38;2;113;102;134:*~=38;2;113;102;134:*.tmp=38;2;113;102;134:*.log=38;2;113;102;134:*.lock=38;2;154;143;173:*.env=38;2;232;200;122:*.env.example=38;2;154;143;173:*.gitignore=38;2;154;143;173:*.dockerignore=38;2;154;143;173:*.editorconfig=38;2;154;143;173
-e 's/38;2;137;180;250/38;2;231;156;254/g' \ LSCOLORS
-e 's/48;2;137;180;250/48;2;231;156;254/g' \
-e 's/38;2;245;194;231/38;2;255;121;198/g' \
-e 's/48;2;245;194;231/48;2;255;121;198/g' \
-e 's/38;2;116;199;236/38;2;139;233;253/g' \
-e 's/48;2;116;199;236/48;2;139;233;253/g' \
-e 's/38;2;88;91;112/38;2;127;132;156/g' \
-e 's/48;2;88;91;112/48;2;127;132;156/g' \
-e 's/38;2;30;30;46/38;2;52;28;74/g' \
-e 's/48;2;30;30;46/48;2;52;28;74/g' \
-e 's/38;2;49;50;68/38;2;61;36;84/g' \
-e 's/48;2;49;50;68/48;2;61;36;84/g' \
-e 's/38;2;203;166;247/38;2;231;156;254/g' \
-e 's/48;2;203;166;247/48;2;231;156;254/g' \
-e 's/38;2;205;214;244/38;2;248;248;242/g' \
-e 's/38;2;180;190;254/38;2;231;156;254/g' \
-e 's/38;2;148;226;213/38;2;139;233;253/g'

View File

@@ -1,32 +1,32 @@
/* ── violet-chaton rofi theme ─────────────────────────────────────────────── */ /* ── violet-chaton v2 rofi theme ──────────────────────────────────────────── */
* { * {
bg0: #261537; bg0: #1a0e27;
bg1: #341c4a; bg1: #261537;
surface: #493161; surface: #493161;
pink: #ff79c6; magenta: #ff4da6;
purple: #e79cfe; lilac: #c9a0ff;
cyan: #8be9fd; lavande: #a4b4ff;
text: #f8f8f2; text: #f0eaf8;
muted: #6c7086; muted: #716686;
overlay: #9399b2; subtext0: #9a8fad;
background-color: transparent; background-color: transparent;
text-color: @text; text-color: @text;
} }
/* ── Fenêtre — positionnée sous les pills ─────────────────────────────────── */ /* ── Fenetre ─────────────────────────────────────────────────────────────── */
window { window {
background-color: rgba(38, 21, 55, 0.94); background-color: rgba(26, 14, 39, 0.94);
border: 2px; border: 2px;
border-color: rgba(255, 121, 198, 0.38); border-color: rgba(255, 77, 166, 0.38);
border-radius: 14px; border-radius: 14px;
width: 500px; width: 500px;
padding: 0px; padding: 0px;
} }
/* ── Layout — résultats en haut, recherche en bas ────────────────────────── */ /* ── Layout — resultats en haut, recherche en bas ────────────────────────── */
mainbox { mainbox {
background-color: transparent; background-color: transparent;
@@ -35,7 +35,7 @@ mainbox {
children: [ listview, inputbar ]; children: [ listview, inputbar ];
} }
/* ── Liste des résultats ─────────────────────────────────────────────────── */ /* ── Liste des resultats ─────────────────────────────────────────────────── */
listview { listview {
background-color: transparent; background-color: transparent;
@@ -56,15 +56,15 @@ element {
} }
element selected { element selected {
background-color: rgba(255, 121, 198, 0.16); background-color: rgba(255, 77, 166, 0.16);
border: 1px; border: 1px;
border-color: rgba(255, 121, 198, 0.32); border-color: rgba(255, 77, 166, 0.32);
} }
element hover { element hover {
background-color: rgba(255, 121, 198, 0.16); background-color: rgba(255, 77, 166, 0.16);
border: 1px; border: 1px;
border-color: rgba(255, 121, 198, 0.32); border-color: rgba(255, 77, 166, 0.32);
} }
element-icon { element-icon {
@@ -80,20 +80,20 @@ element-text {
} }
element-text selected { element-text selected {
text-color: @pink; text-color: @magenta;
} }
element-text hover { element-text hover {
text-color: @pink; text-color: @magenta;
} }
/* ── Séparateur visuel avant la recherche ─────────────────────────────────── */ /* ── Barre de recherche ──────────────────────────────────────────────────── */
inputbar { inputbar {
background-color: rgba(52, 28, 74, 0.75); background-color: rgba(38, 21, 55, 0.75);
border-radius: 0 0 12px 12px; border-radius: 0 0 12px 12px;
border: 1px; border: 1px;
border-color: rgba(92, 73, 108, 0.50); border-color: rgba(90, 56, 117, 0.50);
padding: 9px 14px; padding: 9px 14px;
spacing: 8px; spacing: 8px;
children: [ prompt, entry ]; children: [ prompt, entry ];
@@ -101,30 +101,30 @@ inputbar {
prompt { prompt {
background-color: transparent; background-color: transparent;
text-color: @pink; text-color: @magenta;
font: "JetBrainsMono Nerd Font Bold 14"; font: "Maple Mono NF Bold 14";
vertical-align: 0.5; vertical-align: 0.5;
} }
entry { entry {
background-color: transparent; background-color: transparent;
text-color: @text; text-color: @text;
placeholder: "Rechercher une application..."; placeholder: "Rechercher...";
placeholder-color: @muted; placeholder-color: @muted;
vertical-align: 0.5; vertical-align: 0.5;
} }
/* ── Scrollbar ───────────────────────────────────────────────────────────── */ /* ── Scrollbar ───────────────────────────────────────────────────────────── */
scrollbar { scrollbar {
background-color: rgba(92, 73, 108, 0.30); background-color: rgba(90, 56, 117, 0.30);
handle-color: rgba(255, 121, 198, 0.40); handle-color: rgba(255, 77, 166, 0.40);
handle-width: 4px; handle-width: 4px;
border-radius: 2px; border-radius: 2px;
padding: 0; padding: 0;
} }
/* ── Messages ────────────────────────────────────────────────────────────── */ /* ── Messages ────────────────────────────────────────────────────────────── */
message { message {
background-color: transparent; background-color: transparent;
@@ -132,6 +132,6 @@ message {
} }
textbox { textbox {
text-color: @overlay; text-color: @subtext0;
background-color: transparent; background-color: transparent;
} }

View File

@@ -4,20 +4,20 @@
"accentOnWindow": true, "accentOnWindow": true,
"accentSaturationLimit": 1, "accentSaturationLimit": 1,
"alpha": 1, "alpha": 1,
"backgroundImage": "/resources/bg_pawel-czerwinski-Xk7ktUfyaNI-unsplash.webp", "backgroundImage": "",
"backgroundPosition": "stretch", "backgroundPosition": "stretch",
"backgroundSource": "", "backgroundSource": "",
"blur": 0, "blur": 0,
"colorAccentBg": "#2c183e", "colorAccentBg": "#1a0e27",
"colorBg": "#341c4a", "colorBg": "#261537",
"colorFg": "#d3d9e3", "colorFg": "#f0eaf8",
"colorHighlightBg": "#b68fdc", "colorHighlightBg": "#c9a0ff",
"colorWindowBg": "#1D1E21", "colorWindowBg": "#1a0e27",
"contrast": 0, "contrast": 0,
"dimBlurred": false, "dimBlurred": false,
"engineVersion": 1, "engineVersion": 1,
"id": "11455bed-3b48-4de5-9a03-a83a1b4be775", "id": "11455bed-3b48-4de5-9a03-a83a1b4be775",
"name": "Rice Violet-Chaton", "name": "Violet-Chaton v2",
"preferSystemAccent": false, "preferSystemAccent": false,
"radius": 14, "radius": 14,
"simpleScrollbar": true, "simpleScrollbar": true,

View File

@@ -102,9 +102,11 @@ window#waybar {
#cpu, #cpu,
#temperature, #temperature,
#custom-cpu-temp,
#custom-gpu, #custom-gpu,
#memory, #memory,
#disk, #disk,
#custom-disks,
#custom-network, #custom-network,
#clock, #clock,
#custom-date, #custom-date,
@@ -140,7 +142,8 @@ window#waybar {
/* ── Température ──────────────────────────────────────────────────────────── */ /* ── Température ──────────────────────────────────────────────────────────── */
#temperature { #temperature,
#custom-cpu-temp {
color: rgba(139, 233, 253, 0.60); color: rgba(139, 233, 253, 0.60);
font-size: 11px; font-size: 11px;
font-weight: normal; font-weight: normal;
@@ -148,12 +151,17 @@ window#waybar {
padding-right: 10px; padding-right: 10px;
} }
#temperature.critical { #temperature.critical,
#custom-cpu-temp.critical {
color: #f38ba8; color: #f38ba8;
font-weight: bold; font-weight: bold;
animation: pulse-critical 0.8s linear infinite; animation: pulse-critical 0.8s linear infinite;
} }
#custom-cpu-temp.warning {
color: #f9e2af;
}
/* ── GPU ──────────────────────────────────────────────────────────────────── */ /* ── GPU ──────────────────────────────────────────────────────────────────── */
#custom-gpu { #custom-gpu {
@@ -186,7 +194,8 @@ window#waybar {
/* ── Disque ───────────────────────────────────────────────────────────────── */ /* ── Disque ───────────────────────────────────────────────────────────────── */
#disk { #disk,
#custom-disks {
color: rgba(255, 121, 198, 0.70); color: rgba(255, 121, 198, 0.70);
font-size: 11px; font-size: 11px;
font-weight: normal; font-weight: normal;
@@ -405,6 +414,8 @@ tooltip label {
#cpu:hover, #cpu:hover,
#memory:hover, #memory:hover,
#disk:hover, #disk:hover,
#custom-disks:hover,
#custom-cpu-temp:hover,
#custom-network:hover, #custom-network:hover,
#wireplumber:hover, #wireplumber:hover,
#backlight:hover, #backlight:hover,

View File

@@ -1,96 +1,96 @@
# violet-chaton — thème yazi # violet-chaton v2 — theme yazi
[mgr] [mgr]
cwd = { fg = "#e79cfe" } # purple — dossier courant cwd = { fg = "#c9a0ff" }
find_keyword = { fg = "#f9e2af", bold = true, italic = true, underline = true } find_keyword = { fg = "#e8c87a", bold = true, italic = true, underline = true }
find_position = { fg = "#ff79c6", bg = "reset", bold = true, italic = true } find_position = { fg = "#ff4da6", bg = "reset", bold = true, italic = true }
marker_copied = { fg = "#8be9fd", bg = "#8be9fd" } # cyan marker_copied = { fg = "#a4b4ff", bg = "#a4b4ff" }
marker_cut = { fg = "#f38ba8", bg = "#f38ba8" } # danger marker_cut = { fg = "#f25c7a", bg = "#f25c7a" }
marker_marked = { fg = "#e79cfe", bg = "#e79cfe" } # purple marker_marked = { fg = "#c9a0ff", bg = "#c9a0ff" }
marker_selected = { fg = "#ff79c6", bg = "#ff79c6" } # pink marker_selected = { fg = "#ff4da6", bg = "#ff4da6" }
count_copied = { fg = "#261537", bg = "#8be9fd" } count_copied = { fg = "#261537", bg = "#a4b4ff" }
count_cut = { fg = "#261537", bg = "#f38ba8" } count_cut = { fg = "#261537", bg = "#f25c7a" }
count_selected = { fg = "#261537", bg = "#ff79c6" } count_selected = { fg = "#261537", bg = "#ff4da6" }
border_symbol = "│" border_symbol = "│"
border_style = { fg = "#6c7086" } border_style = { fg = "#716686" }
[tabs] [tabs]
active = { fg = "#261537", bg = "#ff79c6", bold = true } active = { fg = "#261537", bg = "#ff4da6", bold = true }
inactive = { fg = "#ff79c6", bg = "#3d2454" } inactive = { fg = "#ff4da6", bg = "#3d2454" }
[mode] [mode]
normal_main = { fg = "#261537", bg = "#ff79c6", bold = true } normal_main = { fg = "#261537", bg = "#ff4da6", bold = true }
normal_alt = { fg = "#ff79c6", bg = "#3d2454" } normal_alt = { fg = "#ff4da6", bg = "#3d2454" }
select_main = { fg = "#261537", bg = "#8be9fd", bold = true } select_main = { fg = "#261537", bg = "#a4b4ff", bold = true }
select_alt = { fg = "#8be9fd", bg = "#3d2454" } select_alt = { fg = "#a4b4ff", bg = "#3d2454" }
unset_main = { fg = "#261537", bg = "#e79cfe", bold = true } unset_main = { fg = "#261537", bg = "#c9a0ff", bold = true }
unset_alt = { fg = "#e79cfe", bg = "#3d2454" } unset_alt = { fg = "#c9a0ff", bg = "#3d2454" }
[status] [status]
perm_sep = { fg = "#6c7086" } perm_sep = { fg = "#716686" }
perm_type = { fg = "#e79cfe" } # purple perm_type = { fg = "#c9a0ff" }
perm_read = { fg = "#8be9fd" } # cyan perm_read = { fg = "#a4b4ff" }
perm_write = { fg = "#ff79c6" } # pink perm_write = { fg = "#ff4da6" }
perm_exec = { fg = "#a6e3a1" } # green perm_exec = { fg = "#9adba8" }
progress_label = { fg = "#f8f8f2", bold = true } progress_label = { fg = "#f0eaf8", bold = true }
progress_normal = { fg = "#8be9fd", bg = "#3d2454" } progress_normal = { fg = "#a4b4ff", bg = "#3d2454" }
progress_error = { fg = "#f38ba8", bg = "#3d2454" } progress_error = { fg = "#f25c7a", bg = "#3d2454" }
[pick] [pick]
border = { fg = "#ff79c6" } border = { fg = "#ff4da6" }
active = { fg = "#e79cfe", bold = true } active = { fg = "#c9a0ff", bold = true }
inactive = {} inactive = {}
[input] [input]
border = { fg = "#ff79c6" } border = { fg = "#ff4da6" }
title = { fg = "#e79cfe" } title = { fg = "#c9a0ff" }
value = { fg = "#f8f8f2" } value = { fg = "#f0eaf8" }
selected = { reversed = true } selected = { reversed = true }
[completion] [completion]
border = { fg = "#8be9fd" } border = { fg = "#a4b4ff" }
active = { fg = "#ff79c6", bold = true } active = { fg = "#ff4da6", bold = true }
inactive = { fg = "#6c7086" } inactive = { fg = "#716686" }
[tasks] [tasks]
border = { fg = "#ff79c6" } border = { fg = "#ff4da6" }
title = { fg = "#e79cfe" } title = { fg = "#c9a0ff" }
hovered = { fg = "#ff79c6", underline = true } hovered = { fg = "#ff4da6", underline = true }
[which] [which]
mask = { bg = "#261537" } mask = { bg = "#261537" }
cands = { fg = "#8be9fd" } cands = { fg = "#a4b4ff" }
rest = { fg = "#6c7086" } rest = { fg = "#716686" }
desc = { fg = "#e79cfe" } desc = { fg = "#c9a0ff" }
separator = " " separator = " "
separator_style = { fg = "#6c7086" } separator_style = { fg = "#716686" }
[help] [help]
on = { fg = "#ff79c6" } on = { fg = "#ff4da6" }
run = { fg = "#8be9fd" } run = { fg = "#a4b4ff" }
desc = { fg = "#f8f8f2" } desc = { fg = "#f0eaf8" }
hovered = { bg = "#3d2454", bold = true } hovered = { bg = "#3d2454", bold = true }
footer = { fg = "#261537", bg = "#e79cfe" } footer = { fg = "#261537", bg = "#c9a0ff" }
[notify] [notify]
title_info = { fg = "#8be9fd" } title_info = { fg = "#a4b4ff" }
title_warn = { fg = "#f9e2af" } title_warn = { fg = "#e8c87a" }
title_error = { fg = "#f38ba8" } title_error = { fg = "#f25c7a" }
[filetype] [filetype]
rules = [ rules = [
{ mime = "image/*", style = { fg = "#ff79c6" } }, { mime = "image/*", style = { fg = "#ff4da6" } },
{ mime = "video/*", style = { fg = "#e79cfe" } }, { mime = "video/*", style = { fg = "#c9a0ff" } },
{ mime = "audio/*", style = { fg = "#8be9fd" } }, { mime = "audio/*", style = { fg = "#a4b4ff" } },
{ mime = "application/zip", style = { fg = "#f9e2af" } }, { mime = "application/zip", style = { fg = "#e8c87a" } },
{ mime = "application/x-tar", style = { fg = "#f9e2af" } }, { mime = "application/x-tar", style = { fg = "#e8c87a" } },
{ mime = "application/pdf", style = { fg = "#f38ba8" } }, { mime = "application/pdf", style = { fg = "#f25c7a" } },
{ mime = "text/*", style = { fg = "#f8f8f2" } }, { mime = "text/*", style = { fg = "#f0eaf8" } },
] ]

537
README.md
View File

@@ -1,416 +1,201 @@
# violet-chaton — setup automatique # violet-chaton v2
Environnement terminal complet aux couleurs violet-chaton, pensé pour Pop!_OS / Ubuntu avec COSMIC Desktop. > Rice Pop!\_OS complet — palette originale, zero emprunt.
(Bonus thème Vesktop / Discord) > Compatible **COSMIC** et **Hyprland**.
![preview](INSTALL/assets/violet-chaton-logo.png)
--- ---
## Démarrage rapide ## Philosophie
Palette 100% originale inspiree du gradient de **Mitsuri Kanroji** (rose → vert pastel).
Fond violet profond signature, 5 accents uniques, variantes dark + light.
Source de verite unique : `INSTALL/themes/palette.sh`
---
## Install
```bash ```bash
bash install.sh git clone https://git.tetardtek.com/Tetardtek/dotfiles-violet-chaton.git
cd dotfiles-violet-chaton
bash INSTALL/install.sh
``` ```
Un menu s'affiche. Choisis **1** pour une installation complète. Choisir **1** (complete) ou **4** (configs uniquement si les outils sont deja la).
Prerequis : `sudo apt install -y curl git unzip`
--- ---
## Prérequis ## Ce que tu obtiens
- **Distribution :** Debian, Ubuntu, Pop!_OS (ou dérivé apt) ### Terminal
- **Droits :** compte avec `sudo` - **kitty** — GPU-accelerated, ligatures, cursor trail magenta, splits tiling, GLSL shader glow
- **Connexion internet** (téléchargement des binaires) - **Maple Mono NF** — italiques cursives, ligatures, fine et niche
- **Outils de base :**
```bash ### Shell
sudo apt install -y curl git unzip - **zsh** + zinit + autosuggestions + syntax-highlighting (palette v2)
``` - **starship** 3 lignes — separateur style, prompt signature, brain\_name env var
- **vi-mode** — beam/block cursor adaptatif, keybindings emacs preserves
- **atuin** — historique shell fuzzy
> Pour COSMIC Desktop : Pop!_OS 24.04 ou supérieur. ### Outils CLI
| Outil | Remplace | Description |
|-------|----------|-------------|
| `eza` | ls | Listing colore avec icones |
| `bat` | cat | Coloration syntaxique + theme v2 |
| `fd` | find | Recherche de fichiers |
| `fzf` | — | Fuzzy finder (fichiers, dossiers, git log, process killer) |
| `zoxide` | cd | Navigation intelligente |
| `ripgrep` | grep | Recherche dans les fichiers |
| `lazygit` | — | Git TUI avec couleurs v2 |
| `delta` | diff | Diffs colores side-by-side |
| `yazi` | — | Explorateur fichiers TUI + preview images kitty |
| `btop` | top | Monitoring systeme avec theme v2 |
| `dust` | du/ncdu | Analyse disque |
| `procs` | ps | Process viewer |
| `tokei` | — | Stats code par langage |
| `sd` | sed | Remplacement simplifie |
| `hyperfine` | time | Benchmarks CLI |
| `gping` | ping | Ping avec graphe |
### Desktop
- **AGS** (Aylur's GTK Shell) — barre 3-pills glassmorphism, OSD gradient Mitsuri, launcher, notifications
- **Rofi** — backup launcher avec theme v2
- Detection auto compositor (Hyprland = workspaces, COSMIC = mode compatible)
### Theming
- **COSMIC** Dark + Light — palette v2 complete (accent, background, primary, semantiques)
- **COSMIC Term** — Maple Mono NF + color schemes dark/light
- **GTK3/GTK4** — dark + light css
- **Vivaldi** — theme injecte automatiquement
- **Vesktop/Discord** — theme css
- Candy-icons
--- ---
## Ce que fait le script — étape par étape ## Palette v2
### Étape 1 — Paquets apt (`01-packages-apt.sh`) ### Dark
Installe les outils via le gestionnaire de paquets système : | | Hex | Role |
|-|-----|------|
| ![#261537](https://placehold.co/12x12/261537/261537.png) | `#261537` | base — fond principal |
| ![#341c4a](https://placehold.co/12x12/341c4a/341c4a.png) | `#341c4a` | mantle |
| ![#3d2454](https://placehold.co/12x12/3d2454/3d2454.png) | `#3d2454` | surface0 |
| ![#5a3875](https://placehold.co/12x12/5a3875/5a3875.png) | `#5a3875` | surface2 — selection |
| ![#ff4da6](https://placehold.co/12x12/ff4da6/ff4da6.png) | `#ff4da6` | magenta — accent primaire |
| ![#c9a0ff](https://placehold.co/12x12/c9a0ff/c9a0ff.png) | `#c9a0ff` | lilac — accent secondaire |
| ![#9adba8](https://placehold.co/12x12/9adba8/9adba8.png) | `#9adba8` | mitsuri — vert pastel / success |
| ![#a4b4ff](https://placehold.co/12x12/a4b4ff/a4b4ff.png) | `#a4b4ff` | lavande — bleu-violet / info |
| ![#e8c87a](https://placehold.co/12x12/e8c87a/e8c87a.png) | `#e8c87a` | champagne — or chaud / warning |
| ![#f25c7a](https://placehold.co/12x12/f25c7a/f25c7a.png) | `#f25c7a` | danger |
| ![#f0eaf8](https://placehold.co/12x12/f0eaf8/f0eaf8.png) | `#f0eaf8` | text |
| ![#716686](https://placehold.co/12x12/716686/716686.png) | `#716686` | muted |
| Outil | Rôle | ### Gradient signature
|------------------|------|
| `zsh` | Shell principal (remplace bash) |
| `eza` | Remplacement de `ls` avec icônes et couleurs |
| `bat` | Remplacement de `cat` avec coloration syntaxique |
| `fd-find` | Remplacement de `find`, plus rapide et intuitif |
| `fzf` | Fuzzy finder — recherche floue de fichiers et dossiers |
| `zoxide` | Remplacement de `cd` avec mémoire des dossiers fréquents |
| `git-delta` | Pager git avec diff coloré côte à côte |
| `vivid` | Générateur de LS_COLORS |
| `ripgrep` | Remplacement de `grep`, très rapide |
| `ncdu` | Analyse d'espace disque en TUI |
| `thefuck` | Correction automatique de la dernière commande ratée |
| `lolcat` | Arc-en-ciel sur n'importe quel output |
| `cbonsai` | Bonsaï ASCII animé |
| `chafa` | Affichage d'images dans le terminal (logo fastfetch) |
| `cava` | Visualiseur audio animé |
| `btop` | Moniteur système en TUI |
| `nemo` | Explorateur de fichiers GUI |
| `jq` | Processeur JSON en ligne de commande |
| `vivaldi-stable` | Navigateur — dépôt officiel ajouté automatiquement |
| `gh` | CLI GitHub (auth, PR, issues) — dépôt officiel ajouté automatiquement |
| `cmatrix` | Pluie de caractères style Matrix |
| `toilet` | Texte en gros ASCII art coloré (bannières dans le terminal) |
| `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`. ```
magenta → lilac → lavande → mitsuri
#ff4da6 → #c9a0ff → #a4b4ff → #9adba8
```
### Light
Fonds lavande clairs (`#f3edf8`), accents assombris pour contraste WCAG.
Meme identite violet, meme gradient. Voir `palette.sh` section LIGHT.
--- ---
### Étape 2 — Binaires manuels (`02-packages-manual.sh`) ## Raccourcis kitty
Télécharge les versions les plus récentes depuis GitHub et les installe dans `~/.local/bin/` : | Touche | Action |
|--------|--------|
| `Ctrl+Shift+\` | Split vertical |
| `Ctrl+Shift+-` | Split horizontal |
| `Ctrl+Shift+Z` | Zoom (stack toggle) |
| `Ctrl+Shift+←→↑↓` | Naviguer entre splits |
| `Ctrl+Shift+1-5` | Layouts (tall/fat/grid/h/v) |
| `Ctrl+Shift+T` | Nouvel onglet |
| `Ctrl+Shift+W` | Fermer onglet |
| `Ctrl+Shift+R` | Redimensionner split |
| `Ctrl+V` | Coller |
| `Ctrl+C` | Copier / interrompre |
| Outil | Rôle | ## Raccourcis shell
|--------------|------|
| `lazygit` | Interface git complète en TUI (`lg`) |
| `yazi` | Gestionnaire de fichiers en TUI |
| `glow` | Rendu Markdown dans le terminal |
| `tldr` | Man pages simplifiées avec exemples (tealdeer) |
| `navi` | Cheatsheets interactives |
| `pipes.sh` | Animation de tuyaux dans le terminal |
| `fastfetch` | Infos système au démarrage du terminal — `.deb` depuis GitHub |
| `uv` / `uvx` | Gestionnaire de paquets Python ultra-rapide — script officiel astral.sh |
Installe également : | Touche | Action |
- **starship** et **atuin** via leurs scripts officiels |--------|--------|
- **zinit** (gestionnaire de plugins zsh) via git clone | `Ctrl+G` | fzf fichier |
- **Nerd Fonts** — JetBrainsMono NL et 0xProto, vers `~/.local/share/fonts/NerdFonts/` | `Ctrl+F` | fzf dossier |
- **candy-icons** — thème d'icônes, vers `~/.local/share/icons/candy-icons-master/` | `Ctrl+R` | Historique (atuin) |
| `Ctrl+Space` | Accepter suggestion |
| `Escape` | Vi normal mode |
| `i` | Retour insert |
> **nomachine** — à installer manuellement depuis [nomachine.com](https://www.nomachine.com/download) (comme VSCode et Vesktop) ## Commandes custom
Met à jour le cache des pages tldr et le cache de polices (`fc-cache`). | Commande | Description |
|----------|-------------|
| `colors` | Affiche la palette v2 |
| `hotkeys` | Cheatsheet raccourcis |
| `proj` | Project switcher fzf dans ~/Dev |
| `glog` | Git log fzf avec preview |
| `fkill` | Process killer fzf |
| `y` | Yazi (cd au dossier visite) |
| `weather [ville]` | Meteo rapide |
## Alias
| Alias | Commande reelle |
|-------|-----------------|
| `ls` | eza --icons |
| `ll` | eza -l --icons --git |
| `la` | eza -la --icons --git |
| `lt` | eza --tree --level=3 |
| `cat` | bat (sans pager) |
| `bat` | batcat |
| `fd` | fdfind |
| `lg` | lazygit |
| `tl` | tldr |
| `disk` | dust |
| `top` | btop |
| `fetch` | fastfetch + chafa logo |
--- ---
### Étape 3 — Déploiement des configs et thèmes (`03-deploy-configs.sh`) ## Structure
Copie les fichiers de config et thèmes aux bons emplacements.
Avant chaque déploiement, les fichiers existants sont sauvegardés dans :
```
~/.config/violet-chaton-backups/YYYYMMDD-HHMMSS/
```
Le chemin exact est affiché à la fin du script si des fichiers ont été sauvegardés.
**Configs shell :**
- `~/.zshrc` — configuration zsh complète
- `~/.bashrc` — configuration bash minimale (PATH + `exec zsh`)
- `~/.gitconfig` — git avec delta comme pager
> `user.name` et `user.email` présents dans le gitconfig existant sont **automatiquement préservés** après le déploiement.
**Configs outils :**
- `~/.config/starship.toml` — prompt 2 lignes violet-chaton
- `~/.config/bat/config` — thème violet-chaton, style header
- `~/.config/btop/btop.conf` — moniteur avec thème violet-chaton
- `~/.config/fastfetch/config.jsonc` — modules système + logo chafa
- `~/.config/atuin/config.toml` — historique fuzzy, colonnes, thème
- `~/.config/lazygit/config.yml` — couleurs violet-chaton + delta
- `~/.config/yazi/yazi.toml` — config gestionnaire de fichiers
- `~/.config/glow/glow.yml` — style markdown dark
**Thèmes CLI :**
- `~/.config/bat/themes/violet-chaton.tmTheme`
- `~/.config/btop/themes/violet-chaton.theme`
- `~/.config/atuin/themes/violet-chaton.toml`
- `~/.config/cava/config`
- `~/.config/yazi/theme.toml`
- `~/.config/vesktop/themes/violet-chaton.theme.css` — Vesktop natif (toujours déployé)
- `~/.var/app/dev.vencord.Vesktop/config/vesktop/themes/` — Vesktop Flatpak (si installé)
**GTK3 et Nemo :**
- `~/.config/gtk-3.0/gtk.css` — thème GTK3 violet-chaton (Nemo et applications GTK)
- Nemo défini comme gestionnaire de fichiers par défaut (`xdg-mime`)
- Préférences Nemo appliquées via `gsettings` : vue icônes, miniatures, zoom standard
- Thème d'icônes **candy-icons** activé via `gsettings`
**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`
Reconstruit également le **cache bat** pour activer le thème de coloration syntaxique.
---
## Étapes manuelles après installation
### zinit — premier démarrage
Au premier lancement de zsh, zinit télécharge automatiquement les plugins :
- `zsh-autosuggestions` — suggestions en gris au fil de la frappe
- `zsh-syntax-highlighting` — coloration des commandes en temps réel
- `zsh-completions` — complétion étendue
Cela peut prendre quelques secondes lors du tout premier démarrage.
### Polices — vérification
Si les icônes ne s'affichent pas correctement dans le terminal, forcer la reconstruction du cache de polices :
```bash
fc-cache -f -v
```
Puis sélectionner **JetBrainsMono NL Nerd Font** dans les préférences du terminal.
### atuin — synchronisation (optionnel)
atuin peut synchroniser l'historique entre machines. Pour activer la sync :
```bash
atuin register # créer un compte
atuin sync # synchroniser
```
Sans compte, atuin fonctionne en local uniquement.
---
## Log d'installation
Chaque installation génère un fichier log horodaté :
```
~/violet-chaton-install-YYYYMMDD-HHMMSS.log
```
En cas d'erreur, consulter ce fichier pour identifier l'étape qui a échoué.
---
## Troubleshooting
### Icônes ne s'affichent pas
- Vérifier que la police **JetBrainsMono NL Nerd Font** est sélectionnée dans le terminal
- Relancer `fc-cache -f` puis rouvrir le terminal
### zinit ne se lance pas
- Vérifier que `~/.local/share/zinit/zinit.git/zinit.zsh` existe
- Relancer le script d'installation étape 3
### Injection Vivaldi échoue
- Lancer Vivaldi une première fois, le fermer complètement, puis relancer `bash install.sh` → option 4
### Thème bat ne s'applique pas
```bash
bat cache --build
```
### candy-icons ne s'affiche pas dans Nemo
```bash
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
| Raccourci | Action |
|----------------|--------|
| `Ctrl+R` | Historique atuin (fuzzy search) |
| `Ctrl+G` | Rechercher un fichier (fzf) |
| `Ctrl+F` | Naviguer vers un dossier (fzf) |
| `Ctrl+Space` | Accepter la suggestion autosuggestions |
| `→` | Accepter la suggestion mot par mot |
---
## Alias configurés
| Alias | Commande réelle |
|----------|-----------------|
| `ls` | `eza --icons --git --group-directories-first` |
| `ll` | `eza -l --icons --git` |
| `lt` | `eza --tree --icons` |
| `cat` | `batcat --paging=never` |
| `bat` | `batcat` |
| `fd` | `fdfind` |
| `man` | `tldr` |
| `lg` | `lazygit` |
| `rg` | `rg --color=always` |
| `disk` | `ncdu` |
| `fetch` | `fastfetch` avec logo chafa |
| `pipes` | `pipes.sh` |
| `cd` | `zoxide` (avec mémoire) |
| `fuck` | correction auto thefuck |
| `grep` | `grep --color=auto` |
---
## Structure du dossier INSTALL/
``` ```
INSTALL/ INSTALL/
├── install.sh script principal — menu interactif install.sh point d'entree (menu interactif)
├── scripts/ scripts/
├── lib.sh couleurs et fonctions partagées lib.sh fonctions partagees (couleurs palette v2)
├── 01-packages-apt.sh installation apt + Vivaldi 01-packages-apt.sh — paquets apt
├── 02-packages-manual.sh binaires GitHub + Nerd Fonts + candy-icons 02-packages-manual.sh binaires GitHub + fonts + kitty + AGS
└── 03-deploy-configs.sh configs, thèmes, COSMIC, Vivaldi 03-deploy-configs.sh — deploy configs + themes + COSMIC + GTK
├── configs/ copies des fichiers de configuration configs/
├── zshrc zshrc — shell config
├── bashrc starship.toml — prompt 3 lignes
├── gitconfig kitty.conf — terminal + cursor trail + splits
├── starship.toml kitty/violet-chaton-glow.glsl — shader glow optionnel
│ ├── bat.conf ags/ — AGS bar + OSD + launcher + notifications
├── btop.conf ...
├── fastfetch.jsonc themes/
├── atuin.toml palette.sh — SOURCE DE VERITE UNIQUE
├── lazygit.yml violet-chaton-*.{css,toml,theme,rasi,json,sh,tmTheme}
├── yazi.toml cosmic/ — COSMIC Dark + Light + Term + Tk
├── glow.yml assets/
├── autostart/ violet-chaton-logo.png
│ │ ├── 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
├── violet-chaton-bat.tmTheme
├── violet-chaton-btop.theme
├── violet-chaton-atuin.toml
├── violet-chaton-cava.conf
├── violet-chaton-yazi.toml
├── violet-chaton-gtk.css thème GTK3 (Nemo + applications GTK)
├── 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/
└── violet-chaton-discord-theme/ sources SCSS du thème (gitignored)
``` ```
--- ---
## Mettre à jour les configs ## Credits
Après avoir modifié un fichier de config sur ta machine, re-copier vers INSTALL/ : - Palette : originale, inspiree Mitsuri Kanroji (Demon Slayer)
- Font : [Maple Mono NF](https://github.com/subframe7536/maple-font)
```bash - Icones : [candy-icons](https://github.com/EliverLara/candy-icons)
# Exemple : mettre à jour la config starship - Shell : [AGS](https://github.com/Aylur/ags) · [starship](https://starship.rs) · [kitty](https://sw.kovidgoyal.net/kitty/)
cp ~/.config/starship.toml ~/Documents/config-violet-chaton/INSTALL/configs/starship.toml
# 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
```
---
## Palette violet-chaton
| Rôle | Hex |
|------------|-----------|
| Background | `#261537` |
| BG medium | `#341c4a` |
| BG high | `#3d2454` |
| Pink | `#ff79c6` |
| Purple | `#e79cfe` |
| Cyan | `#8be9fd` |
| Text | `#f8f8f2` |
| Muted | `#6c7086` |
| Overlay | `#9399b2` |
| Success | `#a6e3a1` |
| Warning | `#f9e2af` |
| Danger | `#f38ba8` |

461
ags-v1/config.js Normal file
View File

@@ -0,0 +1,461 @@
// ── violet-chaton v2 — AGS config ───────────────────────────────────────────
// Barre + OSD + Launcher + Notifications
// API: AGS v1.8.2 (.hook/.bind/.poll — no connections)
const audio = await Service.import("audio");
const battery = await Service.import("battery");
const network = await Service.import("network");
const systemtray = await Service.import("systemtray");
const mpris = await Service.import("mpris");
const notifications = await Service.import("notifications");
const applications = await Service.import("applications");
// ── Compositor detection ────────────────────────────────────────────────────
const compositor = (() => {
const session = Utils.exec("bash -c 'echo $XDG_CURRENT_DESKTOP'").trim();
if (session.includes("Hyprland")) return "hyprland";
if (session.includes("COSMIC")) return "cosmic";
return "unknown";
})();
print(`[violet-chaton] compositor: ${compositor}`);
// ══════════════════════════════════════════════════════════════════════════════
// BAR
// ══════════════════════════════════════════════════════════════════════════════
const Separator = () => Widget.Label({ className: "separator", label: "│" });
const Clock = () => Widget.Label({ className: "clock" })
.poll(1000, (self) => { self.label = Utils.exec("date +%H:%M"); });
const DateWidget = () => Widget.Label({ className: "date" })
.poll(60000, (self) => { self.label = Utils.exec("date '+%a %d %b'"); });
const CPU = () => Widget.Label({ className: "cpu" })
.poll(2000, (self) => {
const usage = Math.round(
Number(Utils.exec(['bash', '-c', "top -bn1 | awk '/^%Cpu/ {print 100-$8}'"]))
);
self.label = `󰻠 ${usage}%`;
self.toggleClassName("warning", usage > 70);
self.toggleClassName("critical", usage > 90);
});
const RAM = () => Widget.Label({ className: "ram" })
.poll(2000, (self) => {
const used = Utils.exec(['bash', '-c', "free -m | awk '/^Mem:/ {printf \"%.1f\", $3/1024}'"]);
const total = Utils.exec(['bash', '-c', "free -m | awk '/^Mem:/ {printf \"%.1f\", $2/1024}'"]);
const pct = Math.round((parseFloat(used) / parseFloat(total)) * 100);
self.label = `󰑭 ${used}G`;
self.toggleClassName("warning", pct > 70);
self.toggleClassName("critical", pct > 90);
});
const Network = () => Widget.Label({ className: "network" })
.hook(network, (self) => {
if (network.primary === "wifi") {
const wifi = network.wifi;
self.label = `󰤨 ${wifi?.ssid || ""}`;
self.toggleClassName("wifi", true);
self.toggleClassName("disconnected", false);
} else if (network.primary === "wired") {
self.label = "󰈀 Eth";
self.toggleClassName("wifi", false);
self.toggleClassName("disconnected", false);
} else {
self.label = "󰤮 ";
self.toggleClassName("disconnected", true);
}
});
const Volume = () => Widget.Button({
className: "volume",
onClicked: () => { audio.speaker.isMuted = !audio.speaker.isMuted; },
child: Widget.Label()
.hook(audio, (self) => {
const vol = Math.round((audio.speaker?.volume || 0) * 100);
const muted = audio.speaker?.isMuted;
const icon = muted ? "󰝟" : vol > 66 ? "󰕾" : vol > 33 ? "󰖀" : "󰕿";
self.label = `${icon} ${vol}%`;
self.parent?.toggleClassName("muted", muted);
}, "speaker-changed"),
});
const Battery = () => Widget.Label({
className: "battery",
visible: battery.bind("available"),
}).hook(battery, (self) => {
const pct = battery.percent;
const charging = battery.charging;
const icon = charging ? "󰂄" : pct > 80 ? "󰁹" : pct > 60 ? "󰂀" :
pct > 40 ? "󰁾" : pct > 20 ? "󰁻" : "󰂎";
self.label = `${icon} ${pct}%`;
self.toggleClassName("charging", charging);
self.toggleClassName("low", pct <= 20 && !charging);
self.toggleClassName("warning", pct <= 30 && !charging);
});
const Media = () => Widget.Label({ className: "media" })
.hook(mpris, (self) => {
const player = mpris.players[0];
if (!player) {
self.visible = false;
return;
}
self.visible = true;
const artist = player.trackArtists?.join(", ") || "";
const title = player.trackTitle || "";
const icon = player.playBackStatus === "Playing" ? " " : " ";
self.label = `${icon}${artist ? artist + " — " : ""}${title}`.slice(0, 50);
self.toggleClassName("paused", player.playBackStatus !== "Playing");
});
const SysTray = () => Widget.Box({
className: "systray",
children: systemtray.bind("items").as((items) =>
items.map((item) =>
Widget.Button({
child: Widget.Icon({ icon: item.bind("icon"), size: 16 }),
tooltipMarkup: item.bind("tooltip-markup"),
onPrimaryClick: (_, event) => item.activate(event),
onSecondaryClick: (_, event) => item.openMenu(event),
})
)
),
});
const LauncherBtn = () => Widget.Button({
className: "launcher-btn",
label: "󱄅",
onClicked: () => App.toggleWindow("launcher"),
});
const PowerBtn = () => Widget.Button({
className: "power-btn",
label: "⏻",
onClicked: () => Utils.exec("bash -c 'systemctl poweroff'"),
});
const Workspaces = () => {
if (compositor !== "hyprland") return Widget.Box({});
// Hyprland not running — return empty
return Widget.Box({});
};
const Bar = (monitor) => Widget.Window({
name: `bar-${monitor}`,
monitor,
anchor: ["top", "left", "right"],
exclusivity: "exclusive",
className: "bar",
child: Widget.CenterBox({
startWidget: Widget.Box({
className: "modules-left",
children: [
LauncherBtn(),
Separator(),
Workspaces(),
CPU(),
RAM(),
],
}),
centerWidget: Widget.Box({
className: "modules-center",
children: [
Media(),
],
}),
endWidget: Widget.Box({
className: "modules-right",
hpack: "end",
children: [
Network(),
Separator(),
Volume(),
Separator(),
Battery(),
Separator(),
DateWidget(),
Clock(),
Separator(),
SysTray(),
Separator(),
PowerBtn(),
],
}),
}),
});
// ══════════════════════════════════════════════════════════════════════════════
// OSD
// ══════════════════════════════════════════════════════════════════════════════
const VolumeOSD = () => {
const icon = Widget.Label({ className: "icon" });
const progress = Widget.ProgressBar();
const label = Widget.Label({ className: "label" });
return Widget.Box({
className: "osd",
children: [icon, progress, label],
}).hook(audio, (self) => {
const vol = audio.speaker?.volume || 0;
const muted = audio.speaker?.isMuted;
icon.label = muted ? "󰝟" : vol > 0.66 ? "󰕾" : vol > 0.33 ? "󰖀" : "󰕿";
progress.value = vol;
label.label = `${Math.round(vol * 100)}%`;
}, "speaker-changed");
};
const BrightnessOSD = () => {
const icon = Widget.Label({ className: "icon", label: "󰃞" });
const progress = Widget.ProgressBar();
const label = Widget.Label({ className: "label" });
const getBrightness = () => {
try {
const max = Number(Utils.exec("brightnessctl max"));
const cur = Number(Utils.exec("brightnessctl get"));
return max > 0 ? cur / max : 0;
} catch {
return 0;
}
};
return Widget.Box({
className: "osd",
children: [icon, progress, label],
}).poll(500, (self) => {
const val = getBrightness();
progress.value = val;
label.label = `${Math.round(val * 100)}%`;
});
};
const OSD = (monitor) => [
Widget.Window({
name: `osd-volume-${monitor}`,
monitor,
anchor: ["bottom"],
layer: "overlay",
visible: false,
child: VolumeOSD(),
}),
Widget.Window({
name: `osd-brightness-${monitor}`,
monitor,
anchor: ["bottom"],
layer: "overlay",
visible: false,
child: BrightnessOSD(),
}),
];
// ══════════════════════════════════════════════════════════════════════════════
// LAUNCHER
// ══════════════════════════════════════════════════════════════════════════════
const AppItem = (app) => Widget.Button({
className: "app-item",
onClicked: () => {
app.launch();
App.closeWindow("launcher");
},
child: Widget.Box({
children: [
Widget.Icon({ icon: app.iconName || "application-x-executable", size: 24 }),
Widget.Box({
vertical: true,
children: [
Widget.Label({ label: app.name, xalign: 0, truncate: "end" }),
Widget.Label({
label: app.description || "",
xalign: 0,
truncate: "end",
css: "color: #716686; font-size: 11px; font-weight: normal;",
}),
],
}),
],
}),
});
const Launcher = (monitor) => {
let apps = applications.list;
const list = Widget.Box({ vertical: true, spacing: 2 });
const entry = Widget.Entry({
className: "search",
placeholderText: "Rechercher...",
onAccept: () => {
const first = apps[0];
if (first) {
first.launch();
App.closeWindow("launcher");
}
},
onChange: ({ text }) => {
apps = applications.query(text || "");
list.children = apps.slice(0, 12).map(AppItem);
if (apps.length === 0) {
list.children = [Widget.Label({
className: "no-results",
label: "Aucun resultat",
})];
}
},
});
return Widget.Window({
name: "launcher",
monitor,
anchor: ["top"],
layer: "overlay",
visible: false,
keymode: "exclusive",
setup: (self) => {
self.keybind("Escape", () => App.closeWindow("launcher"));
self.hook(App, (_, name, visible) => {
if (name === "launcher" && visible) {
entry.text = "";
apps = applications.list;
list.children = apps.slice(0, 12).map(AppItem);
entry.grab_focus();
}
});
},
child: Widget.Box({
className: "launcher",
vertical: true,
children: [
Widget.Scrollable({
hscroll: "never",
vscroll: "automatic",
css: "min-height: 400px;",
child: list,
}),
entry,
],
}),
});
};
// ══════════════════════════════════════════════════════════════════════════════
// NOTIFICATIONS
// ══════════════════════════════════════════════════════════════════════════════
notifications.popupTimeout = 5000;
notifications.cacheActions = true;
const NotificationIcon = (notif) => {
if (notif.image) {
return Widget.Box({
css: `
min-width: 48px; min-height: 48px;
background-image: url("${notif.image}");
background-size: cover;
background-position: center;
border-radius: 8px;
margin-right: 10px;
`,
});
}
return Widget.Icon({
icon: notif.appIcon || notif.appEntry || "dialog-information",
size: 36,
css: "margin-right: 10px;",
});
};
const Notification = (notif) => Widget.Box({
className: `notification ${notif.urgency}`,
vertical: true,
children: [
Widget.Box({
children: [
NotificationIcon(notif),
Widget.Box({
vertical: true,
hexpand: true,
children: [
Widget.Box({
children: [
Widget.Label({
className: "title",
label: notif.summary,
xalign: 0,
hexpand: true,
truncate: "end",
}),
Widget.Label({
className: "time",
label: new Date(notif.time * 1000)
.toLocaleTimeString("fr-FR", {
hour: "2-digit",
minute: "2-digit",
}),
}),
Widget.Button({
className: "close-btn",
label: "✕",
onClicked: () => notif.close(),
}),
],
}),
Widget.Label({
className: "app-name",
label: notif.appName || "",
xalign: 0,
}),
],
}),
],
}),
...(notif.body ? [Widget.Label({
className: "body",
label: notif.body,
xalign: 0,
wrap: true,
useMarkup: true,
})] : []),
...(notif.actions.length > 0 ? [Widget.Box({
className: "actions",
children: notif.actions.map((action) =>
Widget.Button({
label: action.label,
onClicked: () => notif.invoke(action.id),
})
),
})] : []),
],
});
const Notifications = (monitor) => Widget.Window({
name: `notifications-${monitor}`,
monitor,
anchor: ["top", "right"],
layer: "overlay",
child: Widget.Box({
vertical: true,
css: "min-width: 350px;",
children: notifications.bind("popups").as((popups) =>
popups.map(Notification)
),
}),
});
// ══════════════════════════════════════════════════════════════════════════════
// EXPORT
// ══════════════════════════════════════════════════════════════════════════════
export default {
style: `${App.configDir}/css/style.css`,
windows: [
Bar(0),
...OSD(0),
Launcher(0),
Notifications(0),
],
};

385
ags-v1/css/style.css Normal file
View File

@@ -0,0 +1,385 @@
/* ── violet-chaton v2 — AGS stylesheet ────────────────────────────────────────
*
* Palette :
* crust #1a0e27
* base #261537
* mantle #341c4a
* surface0 #3d2454
* surface1 #493161
* surface2 #5a3875
* magenta #ff4da6 accent primaire
* lilac #c9a0ff accent secondaire
* mitsuri #9adba8 vert pastel
* lavande #a4b4ff bleu-violet
* champagne #e8c87a or chaud
* danger #f25c7a
* text #f0eaf8
* subtext1 #c4b8d4
* subtext0 #9a8fad
* muted #716686
*
* ─────────────────────────────────────────────────────────────────────────── */
/* ── Reset ───────────────────────────────────────────────────────────────── */
* {
font-family: "Maple Mono NF", "MapleMono Nerd Font", monospace;
font-size: 13px;
font-weight: bold;
}
/* ══════════════════════════════════════════════════════════════════════════
* BAR — 3 pills glassmorphism (island floating)
* ══════════════════════════════════════════════════════════════════════════ */
.bar {
background: transparent;
}
.bar .modules-left,
.bar .modules-center,
.bar .modules-right {
background: alpha(#261537, 0.88);
border-radius: 14px;
border: 3px solid alpha(#ff4da6, 0.60);
margin: 8px 4px;
padding: 0 4px;
}
.bar .modules-left:hover,
.bar .modules-center:hover,
.bar .modules-right:hover {
border-color: #ff4da6;
box-shadow: 0 4px 28px alpha(#c9a0ff, 0.18);
}
/* ── Launcher button ─────────────────────────────────────────────────────── */
.bar .launcher-btn {
color: #ff4da6;
font-size: 19px;
padding: 0 14px 0 18px;
min-width: 0;
min-height: 0;
}
.bar .launcher-btn:hover {
color: #c9a0ff;
}
/* ── Separator ───────────────────────────────────────────────────────────── */
.bar .separator {
color: alpha(#f0eaf8, 0.12);
font-size: 11px;
padding: 0 4px;
font-weight: normal;
}
/* ── Clock ───────────────────────────────────────────────────────────────── */
.bar .clock {
color: #ff4da6;
font-weight: 900;
font-size: 14px;
letter-spacing: 0.04em;
padding: 0 10px;
}
.bar .clock:hover {
color: #c9a0ff;
}
/* ── Date ────────────────────────────────────────────────────────────────── */
.bar .date {
color: #a4b4ff;
font-size: 12px;
font-weight: normal;
padding: 0 10px 0 2px;
}
/* ── System modules ──────────────────────────────────────────────────────── */
.bar .cpu { color: #a4b4ff; }
.bar .cpu.warning { color: #e8c87a; }
.bar .cpu.critical { color: #f25c7a; }
.bar .ram { color: #ff4da6; }
.bar .ram.warning { color: #e8c87a; }
.bar .ram.critical { color: #f25c7a; }
.bar .temp {
color: alpha(#a4b4ff, 0.60);
font-size: 11px;
font-weight: normal;
}
.bar .temp.warning { color: #e8c87a; }
.bar .temp.critical { color: #f25c7a; }
/* ── Network ─────────────────────────────────────────────────────────────── */
.bar .network {
color: #a4b4ff;
font-size: 11px;
font-weight: normal;
}
.bar .network.disconnected { color: #f25c7a; }
.bar .network.wifi { color: alpha(#a4b4ff, 0.80); }
/* ── Volume ──────────────────────────────────────────────────────────────── */
.bar .volume { color: #ff4da6; }
.bar .volume.muted { color: alpha(#ff4da6, 0.30); }
/* ── Battery ─────────────────────────────────────────────────────────────── */
.bar .battery { color: #ff4da6; }
.bar .battery.charging { color: #9adba8; }
.bar .battery.low { color: #f25c7a; }
.bar .battery.warning { color: #e8c87a; }
/* ── Media (MPRIS) ───────────────────────────────────────────────────────── */
.bar .media {
color: #c9a0ff;
font-size: 12px;
font-weight: normal;
padding: 0 10px;
}
.bar .media.paused {
color: alpha(#c9a0ff, 0.50);
font-style: italic;
}
/* ── Systray ─────────────────────────────────────────────────────────────── */
.bar .systray { padding: 0 8px; }
.bar .systray .passive { opacity: 0.5; }
/* ── Workspaces (Hyprland only) ──────────────────────────────────────────── */
.bar .workspaces button {
background: transparent;
color: #716686;
min-width: 24px;
min-height: 24px;
border-radius: 8px;
margin: 2px;
padding: 0;
}
.bar .workspaces button.active {
background: alpha(#ff4da6, 0.20);
color: #ff4da6;
border: 1px solid alpha(#ff4da6, 0.40);
}
.bar .workspaces button.occupied {
color: #c9a0ff;
}
.bar .workspaces button:hover {
background: alpha(#c9a0ff, 0.12);
color: #c9a0ff;
}
/* ── Power button ────────────────────────────────────────────────────────── */
.bar .power-btn {
color: #f25c7a;
font-size: 15px;
padding: 0 14px 0 8px;
min-width: 0;
min-height: 0;
}
.bar .power-btn:hover { color: #ff4da6; }
/* ── Hover global modules ────────────────────────────────────────────────── */
.bar .cpu:hover,
.bar .ram:hover,
.bar .temp:hover,
.bar .network:hover,
.bar .volume:hover,
.bar .battery:hover {
color: #c9a0ff;
}
/* ══════════════════════════════════════════════════════════════════════════
* OSD — volume / brightness overlay
* ══════════════════════════════════════════════════════════════════════════ */
.osd {
background: alpha(#261537, 0.92);
border-radius: 14px;
border: 2px solid alpha(#ff4da6, 0.40);
padding: 12px 20px;
margin: 0 0 40px 0;
}
.osd .icon {
color: #ff4da6;
font-size: 24px;
margin-right: 12px;
}
.osd progressbar trough {
background: #3d2454;
border-radius: 8px;
min-height: 8px;
min-width: 200px;
}
.osd progressbar progress {
border-radius: 8px;
min-height: 8px;
background: linear-gradient(to right, #ff4da6, #c9a0ff, #a4b4ff, #9adba8);
}
.osd .label {
color: #f0eaf8;
font-size: 12px;
margin-left: 8px;
}
/* ══════════════════════════════════════════════════════════════════════════
* LAUNCHER — app search
* ══════════════════════════════════════════════════════════════════════════ */
.launcher {
background: alpha(#1a0e27, 0.94);
border-radius: 14px;
border: 2px solid alpha(#ff4da6, 0.38);
padding: 10px;
min-width: 500px;
}
.launcher .search {
background: alpha(#261537, 0.75);
border-radius: 12px;
border: 1px solid alpha(#5a3875, 0.50);
padding: 9px 14px;
color: #f0eaf8;
caret-color: #ff4da6;
font-size: 14px;
}
.launcher .search:focus {
border-color: alpha(#ff4da6, 0.60);
}
.launcher .app-item {
background: transparent;
border-radius: 8px;
padding: 7px 10px;
color: #f0eaf8;
}
.launcher .app-item:hover,
.launcher .app-item:focus {
background: alpha(#ff4da6, 0.16);
border: 1px solid alpha(#ff4da6, 0.32);
}
.launcher .app-item:hover label,
.launcher .app-item:focus label {
color: #ff4da6;
}
.launcher .app-item image {
margin-right: 10px;
}
.launcher .no-results {
color: #716686;
padding: 20px;
}
/* ══════════════════════════════════════════════════════════════════════════
* NOTIFICATIONS
* ══════════════════════════════════════════════════════════════════════════ */
.notification {
background: alpha(#261537, 0.94);
border-radius: 14px;
border: 2px solid alpha(#c9a0ff, 0.30);
padding: 12px;
margin: 8px;
min-width: 350px;
}
.notification .title {
color: #ff4da6;
font-weight: bold;
font-size: 13px;
}
.notification .body {
color: #c4b8d4;
font-weight: normal;
font-size: 12px;
}
.notification .app-name {
color: #716686;
font-size: 11px;
}
.notification .time {
color: #716686;
font-size: 10px;
}
.notification .close-btn {
color: #716686;
font-size: 14px;
min-width: 0;
min-height: 0;
padding: 2px 6px;
border-radius: 6px;
}
.notification .close-btn:hover {
color: #f25c7a;
background: alpha(#f25c7a, 0.12);
}
.notification .actions button {
background: alpha(#5a3875, 0.50);
color: #c9a0ff;
border-radius: 8px;
padding: 4px 12px;
margin: 4px 4px 0 0;
}
.notification .actions button:hover {
background: alpha(#ff4da6, 0.20);
color: #ff4da6;
}
/* ── Urgency levels ──────────────────────────────────────────────────────── */
.notification.critical {
border-color: alpha(#f25c7a, 0.60);
}
.notification.critical .title {
color: #f25c7a;
}
/* ══════════════════════════════════════════════════════════════════════════
* TOOLTIP — shared
* ══════════════════════════════════════════════════════════════════════════ */
tooltip {
background: alpha(#1a0e27, 0.96);
border: 1px solid alpha(#ff4da6, 0.30);
border-radius: 10px;
color: #f0eaf8;
padding: 6px 10px;
}

228
ags-v1/widgets/Bar.js Normal file
View File

@@ -0,0 +1,228 @@
// ── violet-chaton v2 — Bar widget ───────────────────────────────────────────
// 3 pills glassmorphism — modulaire selon compositor
const audio = Service.import("audio");
const battery = Service.import("battery");
const network = Service.import("network");
const systemtray = Service.import("systemtray");
const mpris = Service.import("mpris");
// ── Helpers ─────────────────────────────────────────────────────────────────
const Separator = () => Widget.Label({ className: "separator", label: "│" });
const Clock = () => Widget.Label({
className: "clock",
connections: [[1000, (self) => {
self.label = Utils.exec("date +%H:%M");
}]],
});
const DateWidget = () => Widget.Label({
className: "date",
connections: [[60000, (self) => {
self.label = Utils.exec("date '+%a %d %b'");
}]],
});
// ── System ──────────────────────────────────────────────────────────────────
const CPU = () => Widget.Label({
className: "cpu",
connections: [[2000, (self) => {
const usage = Math.round(
Number(Utils.exec(`bash -c "top -bn1 | awk '/^%Cpu/ {print 100-$8}'"`)
));
self.label = `󰻠 ${usage}%`;
self.toggleClassName("warning", usage > 70);
self.toggleClassName("critical", usage > 90);
}]],
});
const RAM = () => Widget.Label({
className: "ram",
connections: [[2000, (self) => {
const used = Utils.exec(`bash -c "free -m | awk '/^Mem:/ {printf \"%.1f\", $3/1024}'"`)
const total = Utils.exec(`bash -c "free -m | awk '/^Mem:/ {printf \"%.1f\", $2/1024}'"`)
const pct = Math.round((parseFloat(used) / parseFloat(total)) * 100);
self.label = `󰑭 ${used}G`;
self.toggleClassName("warning", pct > 70);
self.toggleClassName("critical", pct > 90);
}]],
});
// ── Network ─────────────────────────────────────────────────────────────────
const Network = () => Widget.Label({
className: "network",
connections: [[network, (self) => {
if (network.primary === "wifi") {
const wifi = network.wifi;
self.label = `󰤨 ${wifi?.ssid || ""}`;
self.toggleClassName("wifi", true);
self.toggleClassName("disconnected", false);
} else if (network.primary === "wired") {
self.label = "󰈀 Eth";
self.toggleClassName("wifi", false);
self.toggleClassName("disconnected", false);
} else {
self.label = "󰤮 ";
self.toggleClassName("disconnected", true);
}
}]],
});
// ── Volume ──────────────────────────────────────────────────────────────────
const Volume = () => Widget.Button({
className: "volume",
onClicked: () => { audio.speaker.isMuted = !audio.speaker.isMuted; },
child: Widget.Label({
connections: [[audio, (self) => {
const vol = Math.round((audio.speaker?.volume || 0) * 100);
const muted = audio.speaker?.isMuted;
const icon = muted ? "󰝟" : vol > 66 ? "󰕾" : vol > 33 ? "󰖀" : "󰕿";
self.label = `${icon} ${vol}%`;
self.parent?.toggleClassName("muted", muted);
}, "speaker-changed"]],
}),
});
// ── Battery ─────────────────────────────────────────────────────────────────
const Battery = () => Widget.Label({
className: "battery",
visible: battery.bind("available"),
connections: [[battery, (self) => {
const pct = battery.percent;
const charging = battery.charging;
const icon = charging ? "󰂄" : pct > 80 ? "󰁹" : pct > 60 ? "󰂀" :
pct > 40 ? "󰁾" : pct > 20 ? "󰁻" : "󰂎";
self.label = `${icon} ${pct}%`;
self.toggleClassName("charging", charging);
self.toggleClassName("low", pct <= 20 && !charging);
self.toggleClassName("warning", pct <= 30 && !charging);
}]],
});
// ── Media ───────────────────────────────────────────────────────────────────
const Media = () => Widget.Label({
className: "media",
connections: [[mpris, (self) => {
const player = mpris.players[0];
if (!player) {
self.visible = false;
return;
}
self.visible = true;
const artist = player.trackArtists?.join(", ") || "";
const title = player.trackTitle || "";
const icon = player.playBackStatus === "Playing" ? " " : " ";
self.label = `${icon}${artist ? artist + " — " : ""}${title}`.slice(0, 50);
self.toggleClassName("paused", player.playBackStatus !== "Playing");
}]],
});
// ── Systray ─────────────────────────────────────────────────────────────────
const SysTray = () => Widget.Box({
className: "systray",
children: systemtray.bind("items").transform((items) =>
items.map((item) =>
Widget.Button({
child: Widget.Icon({ icon: item.bind("icon"), size: 16 }),
tooltipMarkup: item.bind("tooltip-markup"),
onPrimaryClick: (_, event) => item.activate(event),
onSecondaryClick: (_, event) => item.openMenu(event),
})
)
),
});
// ── Launcher button ─────────────────────────────────────────────────────────
const LauncherBtn = () => Widget.Button({
className: "launcher-btn",
label: "󱄅",
onClicked: () => App.toggleWindow("launcher"),
});
// ── Power button ────────────────────────────────────────────────────────────
const PowerBtn = () => Widget.Button({
className: "power-btn",
label: "⏻",
onClicked: () => Utils.exec("bash -c 'systemctl poweroff'"),
});
// ── Workspaces (Hyprland only) ──────────────────────────────────────────────
const Workspaces = (compositor) => {
if (compositor !== "hyprland") return Widget.Box({});
const hyprland = Service.import("hyprland");
return Widget.Box({
className: "workspaces",
children: hyprland.bind("workspaces").transform((ws) =>
ws.sort((a, b) => a.id - b.id)
.filter((w) => w.id > 0)
.map((w) =>
Widget.Button({
onClicked: () => hyprland.messageAsync(`dispatch workspace ${w.id}`),
child: Widget.Label({ label: `${w.id}` }),
className: hyprland.active.workspace.bind("id").transform(
(id) => id === w.id ? "active" : w.windows > 0 ? "occupied" : ""
),
})
)
),
});
};
// ── Bar assembly ────────────────────────────────────────────────────────────
export const Bar = (monitor, compositor) => Widget.Window({
name: `bar-${monitor}`,
monitor,
anchor: ["top", "left", "right"],
exclusivity: "exclusive",
className: "bar",
child: Widget.CenterBox({
startWidget: Widget.Box({
className: "modules-left",
children: [
LauncherBtn(),
Separator(),
Workspaces(compositor),
Separator(),
CPU(),
RAM(),
],
}),
centerWidget: Widget.Box({
className: "modules-center",
children: [
Media(),
],
}),
endWidget: Widget.Box({
className: "modules-right",
hpack: "end",
children: [
Network(),
Separator(),
Volume(),
Separator(),
Battery(),
Separator(),
DateWidget(),
Clock(),
Separator(),
SysTray(),
Separator(),
PowerBtn(),
],
}),
}),
});

View File

@@ -0,0 +1,99 @@
// ── violet-chaton v2 — Launcher widget ──────────────────────────────────────
// App launcher avec recherche fuzzy
const applications = Service.import("applications");
const AppItem = (app) => Widget.Button({
className: "app-item",
onClicked: () => {
app.launch();
App.closeWindow("launcher");
},
child: Widget.Box({
children: [
Widget.Icon({ icon: app.iconName || "application-x-executable", size: 24 }),
Widget.Box({
vertical: true,
children: [
Widget.Label({
label: app.name,
xalign: 0,
truncate: "end",
}),
Widget.Label({
label: app.description || "",
xalign: 0,
truncate: "end",
className: "description",
css: "color: #716686; font-size: 11px; font-weight: normal;",
}),
],
}),
],
}),
});
export const Launcher = (monitor) => {
let apps = applications.list;
const list = Widget.Box({
vertical: true,
spacing: 2,
});
const entry = Widget.Entry({
className: "search",
placeholderText: "Rechercher...",
onAccept: () => {
const first = apps[0];
if (first) {
first.launch();
App.closeWindow("launcher");
}
},
onChange: ({ text }) => {
apps = applications.query(text || "");
list.children = apps.slice(0, 12).map(AppItem);
if (apps.length === 0) {
list.children = [Widget.Label({
className: "no-results",
label: "Aucun resultat",
})];
}
},
});
return Widget.Window({
name: "launcher",
monitor,
anchor: ["top"],
layer: "overlay",
visible: false,
keymode: "exclusive",
setup: (self) => {
self.keybind("Escape", () => App.closeWindow("launcher"));
self.hook(App, (_, name, visible) => {
if (name === "launcher" && visible) {
entry.text = "";
apps = applications.list;
list.children = apps.slice(0, 12).map(AppItem);
entry.grab_focus();
}
});
},
child: Widget.Box({
className: "launcher",
vertical: true,
children: [
Widget.Scrollable({
hscroll: "never",
vscroll: "automatic",
css: "min-height: 400px;",
child: list,
}),
entry,
],
}),
});
};

View File

@@ -0,0 +1,105 @@
// ── violet-chaton v2 — Notifications widget ─────────────────────────────────
// Popup notifications stylees — urgency-aware
const notifications = Service.import("notifications");
// Ne pas déranger
notifications.popupTimeout = 5000;
notifications.cacheActions = true;
const NotificationIcon = (notif) => {
if (notif.image) {
return Widget.Box({
css: `
min-width: 48px; min-height: 48px;
background-image: url("${notif.image}");
background-size: cover;
background-position: center;
border-radius: 8px;
margin-right: 10px;
`,
});
}
return Widget.Icon({
icon: notif.appIcon || notif.appEntry || "dialog-information",
size: 36,
css: "margin-right: 10px;",
});
};
const Notification = (notif) => Widget.Box({
className: `notification ${notif.urgency}`,
vertical: true,
children: [
Widget.Box({
children: [
NotificationIcon(notif),
Widget.Box({
vertical: true,
hexpand: true,
children: [
Widget.Box({
children: [
Widget.Label({
className: "title",
label: notif.summary,
xalign: 0,
hexpand: true,
truncate: "end",
}),
Widget.Label({
className: "time",
label: new Date(notif.time * 1000)
.toLocaleTimeString("fr-FR", {
hour: "2-digit",
minute: "2-digit",
}),
}),
Widget.Button({
className: "close-btn",
label: "✕",
onClicked: () => notif.close(),
}),
],
}),
Widget.Label({
className: "app-name",
label: notif.appName || "",
xalign: 0,
}),
],
}),
],
}),
...(notif.body ? [Widget.Label({
className: "body",
label: notif.body,
xalign: 0,
wrap: true,
useMarkup: true,
})] : []),
...(notif.actions.length > 0 ? [Widget.Box({
className: "actions",
children: notif.actions.map((action) =>
Widget.Button({
label: action.label,
onClicked: () => notif.invoke(action.id),
})
),
})] : []),
],
});
export const Notifications = (monitor) => Widget.Window({
name: `notifications-${monitor}`,
monitor,
anchor: ["top", "right"],
layer: "overlay",
child: Widget.Box({
vertical: true,
css: "min-width: 350px;",
children: notifications.bind("popups").transform((popups) =>
popups.map(Notification)
),
}),
});

95
ags-v1/widgets/OSD.js Normal file
View File

@@ -0,0 +1,95 @@
// ── violet-chaton v2 — OSD widget ───────────────────────────────────────────
// Overlay volume / brightness avec gradient Mitsuri
const audio = Service.import("audio");
// ── OSD reveal timer ────────────────────────────────────────────────────────
let osdTimeout = null;
const showOSD = (window) => {
window.visible = true;
if (osdTimeout) clearTimeout(osdTimeout);
osdTimeout = setTimeout(() => {
window.visible = false;
}, 1500);
};
// ── Volume OSD ──────────────────────────────────────────────────────────────
const VolumeOSD = () => {
const icon = Widget.Label({ className: "icon" });
const progress = Widget.ProgressBar();
const label = Widget.Label({ className: "label" });
const box = Widget.Box({
className: "osd",
children: [icon, progress, label],
connections: [[audio, (self) => {
const vol = audio.speaker?.volume || 0;
const muted = audio.speaker?.isMuted;
icon.label = muted ? "󰝟" : vol > 0.66 ? "󰕾" : vol > 0.33 ? "󰖀" : "󰕿";
progress.value = vol;
label.label = `${Math.round(vol * 100)}%`;
}, "speaker-changed"]],
});
return box;
};
// ── Brightness OSD ──────────────────────────────────────────────────────────
const BrightnessOSD = () => {
const icon = Widget.Label({ className: "icon", label: "󰃞" });
const progress = Widget.ProgressBar();
const label = Widget.Label({ className: "label" });
const getBrightness = () => {
try {
const max = Number(Utils.exec("brightnessctl max"));
const cur = Number(Utils.exec("brightnessctl get"));
return max > 0 ? cur / max : 0;
} catch {
return 0;
}
};
const box = Widget.Box({
className: "osd",
children: [icon, progress, label],
connections: [[500, (self) => {
const val = getBrightness();
progress.value = val;
label.label = `${Math.round(val * 100)}%`;
}]],
});
return box;
};
// ── OSD windows ─────────────────────────────────────────────────────────────
export const OSD = (monitor) => {
const volumeWin = Widget.Window({
name: `osd-volume-${monitor}`,
monitor,
anchor: ["bottom"],
layer: "overlay",
visible: false,
child: VolumeOSD(),
});
const brightnessWin = Widget.Window({
name: `osd-brightness-${monitor}`,
monitor,
anchor: ["bottom"],
layer: "overlay",
visible: false,
child: BrightnessOSD(),
});
// Auto-show on volume change
Utils.merge([audio.speaker?.bind("volume")], () => showOSD(volumeWin));
return [volumeWin, brightnessWin];
};

2
ags-v3/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
node_modules/
@girs/

33
ags-v3/app.ts Normal file
View File

@@ -0,0 +1,33 @@
import app from "ags/gtk3/app"
import style from "./style.scss"
import Heartbeat from "./widget/Heartbeat"
import Bar from "./widget/Bar"
import BrainPower from "./widget/panels/BrainPower"
app.start({
css: style,
main() {
const monitors = app.get_monitors()
for (const monitor of monitors) {
Heartbeat(monitor)
Bar(monitor)
}
// Brain Power on primary monitor only
const primary = monitors[0]
if (primary) BrainPower(primary)
},
requestHandler(request: any, res: (response: any) => void) {
const cmd = String(request)
if (cmd.includes("toggle-brain")) {
const win = app.get_window("brain-power")
if (win) {
win.visible = !win.visible
res("toggled")
} else {
res("window not found")
}
} else {
res(`unknown: '${cmd}'`)
}
},
})

21
ags-v3/env.d.ts vendored Normal file
View File

@@ -0,0 +1,21 @@
declare const SRC: string
declare module "inline:*" {
const content: string
export default content
}
declare module "*.scss" {
const content: string
export default content
}
declare module "*.blp" {
const content: string
export default content
}
declare module "*.css" {
const content: string
export default content
}

122
ags-v3/lib/brain.ts Normal file
View File

@@ -0,0 +1,122 @@
import GLib from "gi://GLib"
import { exec } from "ags/process"
const BRAIN_ROOT = GLib.getenv("BRAIN_ROOT")
|| readFile(GLib.get_home_dir() + "/.config/brain-path")?.trim()
|| GLib.get_home_dir() + "/Dev/Brain"
export interface BrainState {
focus: string
todos: { open: number; done: number; top3: string[] }
session: string | null
intentions: { active: number; names: string[] }
repos: { name: string; status: string }[]
commits: string[]
kernelVersion: string
}
function readFile(path: string): string | null {
try {
const [ok, contents] = GLib.file_get_contents(path)
if (ok && contents) {
return new TextDecoder().decode(contents)
}
} catch {}
return null
}
function sh(cmd: string): string {
try { return exec(`bash -c "${cmd}"`).trim() } catch { return "" }
}
export function getFocus(): string {
const content = readFile(`${BRAIN_ROOT}/focus.md`)
if (!content) return "no focus"
const lines = content.split("\n").filter(
(l) => !l.startsWith("#") && !l.startsWith(">") && l.trim() !== "" && !l.startsWith("---")
)
return lines[0]?.trim() || "no focus"
}
export function getTodos(): { open: number; done: number; top3: string[] } {
const content = readFile(`${BRAIN_ROOT}/todo/README.md`)
if (!content) return { open: 0, done: 0, top3: [] }
const lines = content.split("\n")
const open: string[] = []
let done = 0
for (const line of lines) {
const trimmed = line.trim()
if (trimmed.startsWith("- [ ]") || trimmed.startsWith("⬜")) {
open.push(trimmed.replace(/^- \[ \] /, "").replace(/^⬜ ?/, ""))
} else if (trimmed.startsWith("- [x]") || trimmed.startsWith("✅")) {
done++
}
}
return { open: open.length, done, top3: open.slice(0, 3) }
}
export function getSession(): string | null {
try {
const [ok, contents] = GLib.file_get_contents(
GLib.get_home_dir() + "/.claude/session-role"
)
if (ok && contents) {
return new TextDecoder().decode(contents).trim()
}
} catch {}
return null
}
export function getIntentions(): { active: number; names: string[] } {
try {
const out = sh(`grep -rl 'status: active' ${BRAIN_ROOT}/intentions/*.yml 2>/dev/null | while read f; do grep '^name:' "$f" | head -1 | sed 's/name: //'; done`)
const names = out.split("\n").filter(Boolean)
return { active: names.length, names: names.slice(0, 5) }
} catch {
return { active: 0, names: [] }
}
}
export function getRepoStatus(): { name: string; status: string }[] {
const repos = [
{ name: "brain", path: BRAIN_ROOT },
{ name: "toolkit", path: `${BRAIN_ROOT}/toolkit` },
{ name: "progression", path: `${BRAIN_ROOT}/progression` },
]
return repos.map(({ name, path }) => {
const count = sh(`git -C ${path} status --short 2>/dev/null | wc -l`)
const n = parseInt(count) || 0
return {
name,
status: n === 0 ? "✅" : `⚠️ ${n} fichiers`,
}
})
}
export function getRecentCommits(): string[] {
const out = sh(`git -C ${BRAIN_ROOT} log --oneline -5 2>/dev/null`)
return out.split("\n").filter(Boolean)
}
export function getKernelVersion(): string {
const content = readFile(`${BRAIN_ROOT}/brain-compose.yml`)
if (!content) return "?"
const match = content.match(/^version:\s*"?([^"\n]+)"?/m)
return match ? match[1] : "?"
}
export function getBrainState(): BrainState {
return {
focus: getFocus(),
todos: getTodos(),
session: getSession(),
intentions: getIntentions(),
repos: getRepoStatus(),
commits: getRecentCommits(),
kernelVersion: getKernelVersion(),
}
}

10
ags-v3/package.json Normal file
View File

@@ -0,0 +1,10 @@
{
"dependencies": {
"ags": "*",
"gnim": "*"
},
"prettier": {
"semi": false,
"tabWidth": 2
}
}

134
ags-v3/style.scss Normal file
View File

@@ -0,0 +1,134 @@
@use "styles/palette" as *;
@use "styles/heartbeat";
@use "styles/bar";
* {
font-family: $font;
font-size: 16px;
font-weight: bold;
}
// ── Layer 3 — Brain Power Panel ──
window.BrainPower {
background: transparent;
.brain-panel {
background: rgba(26, 14, 39, 0.94); // $crust heavy glass
border-radius: 0 $radius $radius 0;
border: 2px solid rgba(201, 160, 255, 0.40); // $lilac border
border-left: none;
min-width: 380px;
padding: 16px;
}
.brain-header {
padding: 0 0 12px 0;
}
.brain-title {
color: $magenta;
font-size: 16px;
font-weight: 900;
letter-spacing: 0.08em;
}
.brain-close {
background: transparent;
border: none;
min-width: 0;
min-height: 0;
padding: 2px 8px;
label {
color: $muted;
font-size: 14px;
}
&:hover label {
color: $danger;
}
}
.brain-content {
padding: 0 4px;
}
.brain-section {
padding: 8px 0 4px 0;
}
.brain-section-title {
color: $lilac;
font-size: 11px;
font-weight: 900;
letter-spacing: 0.12em;
}
.brain-divider {
background: rgba(90, 56, 117, 0.30); // $surface2
min-height: 1px;
margin: 8px 0;
}
.brain-focus {
color: $text;
font-size: 13px;
padding: 4px 0 4px 16px;
}
.brain-session {
color: $champagne;
font-size: 12px;
font-weight: normal;
padding: 4px 0 4px 16px;
}
.brain-todos-count {
color: $muted;
font-size: 11px;
font-weight: normal;
}
.brain-todos-list {
color: $subtext1;
font-size: 12px;
font-weight: normal;
padding: 4px 0 4px 16px;
}
.brain-version {
color: $muted;
font-size: 12px;
font-weight: normal;
}
.brain-intentions-list {
color: $mitsuri;
font-size: 13px;
font-weight: normal;
padding: 4px 0 4px 16px;
}
.brain-repos-status {
color: $subtext1;
font-size: 13px;
font-weight: normal;
font-family: "Maple Mono NF", monospace;
padding: 4px 0 4px 16px;
}
.brain-commits-list {
color: $subtext0;
font-size: 12px;
font-weight: normal;
font-family: "Maple Mono NF", monospace;
padding: 4px 0 4px 16px;
}
.brain-terminal {
background: #1a0e27;
border-radius: 0 0 $radius 0;
padding: 4px;
min-height: 400px;
}
}

194
ags-v3/styles/_bar.scss Normal file
View File

@@ -0,0 +1,194 @@
@use "palette" as *;
// Layer 1 — Ghost bar (hover reveal)
window.Bar {
background: transparent;
> centerbox {
background: rgba(38, 21, 55, 0.88); // $base @ 0.88
border-radius: $radius;
border: 3px solid rgba(255, 77, 166, 0.60); // $magenta @ 0.60
margin: 6px 8px;
padding: 0 8px;
min-height: 48px;
}
.modules-left,
.modules-center,
.modules-right {
padding: 0 4px;
}
.module {
padding: 0 8px;
color: $text;
}
.prompt-name {
color: $magenta;
font-size: 17px;
padding: 0 0 0 14px;
}
.prompt-cursor {
color: $lilac;
font-size: 17px;
font-weight: 900;
}
.separator {
color: rgba(240, 234, 248, 0.12); // $text @ 0.12
font-size: 14px;
padding: 0 6px;
font-weight: normal;
}
// ── clock ──
.clock {
color: $magenta;
font-weight: 900;
font-size: 18px;
letter-spacing: 0.04em;
padding: 0 10px;
}
.date {
color: $lavande;
font-size: 15px;
font-weight: normal;
padding: 0 10px 0 2px;
}
// ── system stats ──
.cpu { color: $lavande; }
.ram { color: $magenta; }
.temp {
color: rgba(164, 180, 255, 0.60); // $lavande @ 0.60
font-size: 11px;
font-weight: normal;
}
.gpu { color: $mitsuri; }
.cpu, .ram, .gpu, .temp {
&.warning { color: $champagne; }
&.critical { color: $danger; }
}
// ── network ──
.network {
color: $lavande;
font-size: 15px;
padding: 0 10px;
&.disconnected { color: $danger; }
}
// ── volume ──
button.volume,
button.muted {
background: transparent;
border: none;
padding: 0 8px;
min-width: 0;
min-height: 0;
label { color: $magenta; }
&.muted label { color: rgba(255, 77, 166, 0.30); }
&:hover label { color: $lilac; }
}
// ── battery ──
.battery {
color: $magenta;
padding: 0 8px;
&.charging { color: $mitsuri; }
&.low { color: $danger; }
&.warning { color: $champagne; }
}
// ── media ──
.media-module {
padding: 0 4px;
}
.media {
padding: 0 4px;
&.paused .media-text {
color: rgba(201, 160, 255, 0.50); // $lilac @ 0.50
font-style: italic;
}
}
.media-text {
color: $lilac;
font-size: 15px;
font-weight: normal;
padding: 0 6px;
}
.media-prev,
.media-play,
.media-next {
background: transparent;
border: none;
padding: 0 3px;
min-width: 0;
min-height: 0;
label { color: $lilac; font-size: 16px; }
&:hover label { color: $magenta; }
}
// ── systray ──
.systray {
padding: 0 4px;
}
.systray-item {
background: transparent;
border: none;
padding: 0 3px;
min-width: 0;
min-height: 0;
&:hover {
background: rgba(201, 160, 255, 0.12);
border-radius: $radius-sm;
}
}
// ── hover effects ──
.module:hover,
.clock:hover,
.cpu:hover,
.ram:hover,
.network:hover,
.battery:hover {
color: $lilac;
}
// ── workspaces ──
.workspaces button {
background: transparent;
color: $muted;
min-width: 22px;
min-height: 22px;
border-radius: $radius-sm;
margin: 2px;
padding: 0;
&.active {
background: rgba(255, 77, 166, 0.20);
color: $magenta;
border: 1px solid rgba(255, 77, 166, 0.40);
}
&.occupied { color: $lilac; }
&:hover {
background: rgba(201, 160, 255, 0.12);
color: $lilac;
}
}
}

View File

@@ -0,0 +1,21 @@
@use "palette" as *;
// Layer 0 — heartbeat line
// Fin trait magenta en haut de l'écran — "le système est vivant"
window.Heartbeat {
background: transparent;
> box {
// gradient uses rgba() which SCSS understands natively
background: linear-gradient(
to right,
rgba(255, 77, 166, 0.0),
rgba(255, 77, 166, 0.6),
rgba(201, 160, 255, 0.8),
rgba(255, 77, 166, 0.6),
rgba(255, 77, 166, 0.0)
);
min-height: 2px;
}
}

View File

@@ -0,0 +1,35 @@
// ── violet-chaton v2 ──────────────────────────────────────────────────────
// Mitsuri Kanroji inspired — gradient magenta → green
// Ghost Shell edition
// backgrounds
$crust: #1a0e27;
$base: #261537;
$mantle: #341c4a;
$surface0: #3d2454;
$surface1: #493161;
$surface2: #5a3875;
// accents
$magenta: #ff4da6;
$lilac: #c9a0ff;
$mitsuri: #9adba8;
$lavande: #a4b4ff;
$champagne: #e8c87a;
$danger: #f25c7a;
// text
$text: #f0eaf8;
$subtext1: #c4b8d4;
$subtext0: #9a8fad;
$muted: #716686;
// shared
$font: "Maple Mono NF", "MapleMono Nerd Font", monospace;
$radius: 14px;
$radius-sm: 8px;
$transition: 300ms ease-out;
// Note: alpha() is a GTK CSS function, not SCSS.
// Use it raw in GTK CSS values, not in SCSS variables.
// For SCSS variables that need transparency, use rgba().

32
ags-v3/toggle-brain.sh Executable file
View File

@@ -0,0 +1,32 @@
#!/bin/bash
# Toggle Brain Power — dashboard AGS + terminal Kitty (single instance)
BRAIN_ROOT="${BRAIN_ROOT:-$HOME/Dev/Brain}"
KITTY_CLASS="brain-hud-terminal"
# Check if brain kitty is already running (by window class)
kitty_pid=$(pgrep -f "class $KITTY_CLASS" | head -1)
if [ -n "$kitty_pid" ]; then
# Close everything
ags request "toggle-brain" 2>/dev/null
kill "$kitty_pid" 2>/dev/null
else
# Open everything
ags request "toggle-brain" 2>/dev/null
kitty \
--class "$KITTY_CLASS" \
--title "🧠 Brain HUD" \
--override remember_window_size=no \
--override initial_window_width=60c \
--override initial_window_height=30c \
--override background_opacity=0.94 \
--override background=#1a0e27 \
--override foreground=#f0eaf8 \
--override cursor=#ff4da6 \
--override font_size=13 \
--override confirm_os_window_close=0 \
--directory "$BRAIN_ROOT" \
-e zsh -c "echo '🧠 Brain HUD — navigate mode'; echo ''; exec zsh" \
&
disown
fi

14
ags-v3/tsconfig.json Normal file
View File

@@ -0,0 +1,14 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"strict": true,
"module": "ES2022",
"target": "ES2020",
"lib": ["ES2023"],
"moduleResolution": "Bundler",
// "checkJs": true,
// "allowJs": true,
"jsx": "react-jsx",
"jsxImportSource": "ags/gtk3"
}
}

70
ags-v3/widget/Bar.tsx Normal file
View File

@@ -0,0 +1,70 @@
import app from "ags/gtk3/app"
import { Astal, Gtk, Gdk } from "ags/gtk3"
import Clock from "./modules/Clock"
// import Battery from "./modules/Battery" // desktop — no battery
import Volume from "./modules/Volume"
import Network from "./modules/Network"
import SystemStats from "./modules/SystemStats"
import Media from "./modules/Media"
// import SysTray from "./modules/SysTray" // TODO: needs astal-tray (appmenu-glib-translator)
import Prompt from "./modules/Prompt"
export default function Bar(gdkmonitor: Gdk.Monitor) {
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor
let hideTimeout: number | null = null
function scheduleHide(win: Astal.Window) {
if (hideTimeout) clearTimeout(hideTimeout)
hideTimeout = setTimeout(() => {
win.visible = false
hideTimeout = null
}, 800)
}
function cancelHide() {
if (hideTimeout) {
clearTimeout(hideTimeout)
hideTimeout = null
}
}
return (
<window
class="Bar"
name="bar"
visible={true}
gdkmonitor={gdkmonitor}
exclusivity={Astal.Exclusivity.EXCLUSIVE}
anchor={TOP | LEFT | RIGHT}
application={app}
layer={Astal.Layer.TOP}
>
<eventbox
onHover={() => cancelHide()}
onHoverLost={(_self) => {
// disabled for debug — auto-hide off
}}
>
<centerbox>
<box $type="start" class="modules-left" halign={Gtk.Align.START}>
<Prompt />
<label class="separator" label="│" />
<SystemStats />
<label class="separator" label="│" />
<Media />
</box>
<box $type="center" class="modules-center">
<Clock />
</box>
<box $type="end" class="modules-right" halign={Gtk.Align.END}>
{/* <SysTray /> */}
<Network />
<label class="separator" label="│" />
<Volume />
</box>
</centerbox>
</eventbox>
</window>
)
}

View File

@@ -0,0 +1,27 @@
import app from "ags/gtk3/app"
import { Astal, Gtk, Gdk } from "ags/gtk3"
export default function Heartbeat(gdkmonitor: Gdk.Monitor) {
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor
return (
<window
class="Heartbeat"
gdkmonitor={gdkmonitor}
exclusivity={Astal.Exclusivity.NONE}
anchor={TOP | LEFT | RIGHT}
application={app}
layer={Astal.Layer.TOP}
>
<eventbox
onHover={() => {
// reveal the bar
const bar = app.get_window("bar")
if (bar) bar.visible = true
}}
>
<box class="heartbeat-line" />
</eventbox>
</window>
)
}

View File

@@ -0,0 +1,30 @@
import AstalBattery from "gi://AstalBattery"
import { createBinding } from "ags"
export default function Battery() {
const bat = AstalBattery.get_default()
const text = createBinding(bat, "percentage")((p: number) => {
const pct = Math.round(p * 100)
const isCharging = bat.charging
let icon = ""
if (isCharging) icon = "󰂄"
else if (pct > 90) icon = "󰁹"
else if (pct > 70) icon = "󰂁"
else if (pct > 50) icon = "󰁿"
else if (pct > 30) icon = "󰁽"
else if (pct > 10) icon = "󰁻"
else icon = "󰂃"
return `${icon} ${pct}%`
})
const cls = createBinding(bat, "percentage")((p: number) => {
const pct = Math.round(p * 100)
if (bat.charging) return "battery charging"
if (pct <= 10) return "battery low"
if (pct <= 20) return "battery warning"
return "battery"
})
return <label class={cls} label={text} />
}

View File

@@ -0,0 +1,72 @@
import { createPoll } from "ags/time"
import { exec } from "ags/process"
function formatTime(): string {
const now = new Date()
return now.toLocaleTimeString("fr-FR", {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
})
}
function formatDate(): string {
const now = new Date()
return now.toLocaleDateString("fr-FR", {
weekday: "long",
day: "numeric",
month: "long",
year: "numeric",
})
}
function formatDateShort(): string {
const now = new Date()
return now.toLocaleDateString("fr-FR", {
weekday: "short",
day: "numeric",
month: "short",
})
}
function getCalendar(): string {
try {
return exec("bash -c \"cal -h\"").trim()
} catch {
return ""
}
}
function getUptime(): string {
try {
return exec("bash -c \"uptime -p | sed 's/up //'\"").trim()
} catch {
return ""
}
}
function buildTooltip(): string {
const full = formatDate()
const cal = getCalendar()
const up = getUptime()
return `${full}\n\n${cal}\n\n⏱ uptime: ${up}`
}
export default function Clock() {
const time = createPoll("", 1000, () => formatTime())
const date = createPoll("", 60000, () => formatDateShort())
return (
<box>
<label class="clock" label={time} />
<label class="separator" label="│" />
<eventbox
onHover={(self) => {
self.tooltipText = buildTooltip()
}}
>
<label class="date" label={date} hasTooltip />
</eventbox>
</box>
)
}

View File

@@ -0,0 +1,54 @@
import AstalMpris from "gi://AstalMpris"
import { createBinding, For } from "ags"
export default function Media() {
const mpris = AstalMpris.get_default()
const players = createBinding(mpris, "players")
// Filter to only show non-playerctld players
const filtered = players((list: AstalMpris.Player[]) =>
list.filter((p) => p.busName && !p.busName.includes("playerctld")).slice(0, 1)
)
return (
<box class="media-module">
<For each={filtered}>
{(player) => {
const title = createBinding(player, "title")
const status = createBinding(player, "playbackStatus")
const icon = status((s: AstalMpris.PlaybackStatus) =>
s === AstalMpris.PlaybackStatus.PLAYING ? "▶" : "⏸"
)
const label = title((t: string) => {
const a = player.artist
const display = a ? `${a}${t}` : t
return display.length > 45
? display.substring(0, 42) + "..."
: display
})
const cls = status((s: AstalMpris.PlaybackStatus) =>
s === AstalMpris.PlaybackStatus.PLAYING ? "media" : "media paused"
)
return (
<box class={cls}>
<button class="media-prev" onClicked={() => player.previous()}>
<label label="⏮" />
</button>
<button class="media-play" onClicked={() => player.play_pause()}>
<label label={icon} />
</button>
<button class="media-next" onClicked={() => player.next()}>
<label label="⏭" />
</button>
<label class="media-text" label={label} />
</box>
)
}}
</For>
</box>
)
}

View File

@@ -0,0 +1,50 @@
import AstalNetwork from "gi://AstalNetwork"
import { createBinding } from "ags"
import { exec } from "ags/process"
function getIPs(): string {
try {
const lan = exec("bash -c \"hostname -I | awk '{print $1}'\"").trim()
const wan = exec("bash -c \"curl -4 -s --max-time 2 ifconfig.me\"").trim()
return `LAN: ${lan || "?"}\nWAN: ${wan || "?"}`
} catch {
return "IPs unavailable"
}
}
export default function Network() {
const net = AstalNetwork.get_default()
const text = createBinding(net, "primary")((type: AstalNetwork.Primary) => {
if (type === AstalNetwork.Primary.WIFI) {
const wifi = net.wifi
if (!wifi) return "󰤮 offline"
const ssid = wifi.ssid || "wifi"
const strength = wifi.strength
let icon = "󰤯"
if (strength > 80) icon = "󰤨"
else if (strength > 60) icon = "󰤥"
else if (strength > 40) icon = "󰤢"
else if (strength > 20) icon = "󰤟"
return `${icon} ${ssid}`
}
if (type === AstalNetwork.Primary.WIRED) return "󰈀 wired"
return "󰤮 offline"
})
const cls = createBinding(net, "primary")((type: AstalNetwork.Primary) => {
if (type === AstalNetwork.Primary.WIFI) return "network module wifi"
if (type === AstalNetwork.Primary.WIRED) return "network module wired"
return "network module disconnected"
})
return (
<eventbox
onHover={(self) => {
self.tooltipText = getIPs()
}}
>
<label class={cls} label={text} hasTooltip />
</eventbox>
)
}

View File

@@ -0,0 +1,18 @@
import GLib from "gi://GLib"
import { createPoll } from "ags/time"
export default function Prompt() {
const username = GLib.get_user_name() || "user"
const cursor = createPoll("_", 600, () => {
// alternate between _ and empty to create blink
return Date.now() % 1200 < 600 ? "_" : " "
})
return (
<box>
<label class="prompt-name" label={username} />
<label class="prompt-cursor" label={cursor} />
</box>
)
}

View File

@@ -0,0 +1,24 @@
import AstalTray from "gi://AstalTray"
import Gtk from "gi://Gtk?version=3.0"
import { createBinding, For } from "ags"
export default function SysTray() {
const tray = AstalTray.get_default()
const items = createBinding(tray, "items")
return (
<box class="systray">
<For each={items}>
{(item) => (
<button
class="systray-item"
tooltipText={createBinding(item, "tooltipMarkup")}
onClicked={() => item.activate(0, 0)}
>
<icon pixelSize={16} gicon={createBinding(item, "gicon")} />
</button>
)}
</For>
</box>
)
}

View File

@@ -0,0 +1,89 @@
import GLib from "gi://GLib"
import { createPoll } from "ags/time"
import { exec } from "ags/process"
function readProc(path: string): string | null {
try {
const [ok, contents] = GLib.file_get_contents(path)
if (ok && contents) return new TextDecoder().decode(contents)
} catch {}
return null
}
let prevIdle = 0
let prevTotal = 0
function getCpuUsage(): string {
const content = readProc("/proc/stat")
if (!content) return "?"
const line = content.split("\n")[0]
const parts = line.split(/\s+/).slice(1).map(Number)
const idle = parts[3] + parts[4]
const total = parts.reduce((a, b) => a + b, 0)
const dIdle = idle - prevIdle
const dTotal = total - prevTotal
prevIdle = idle
prevTotal = total
if (dTotal === 0) return "0"
return `${Math.round(((dTotal - dIdle) / dTotal) * 100)}`
}
function getRamUsage(): { pct: string; used: string; total: string } {
const content = readProc("/proc/meminfo")
if (!content) return { pct: "?", used: "?", total: "?" }
const lines = content.split("\n")
let totalKb = 0, availableKb = 0
for (const line of lines) {
if (line.startsWith("MemTotal:")) totalKb = parseInt(line.split(/\s+/)[1])
if (line.startsWith("MemAvailable:")) availableKb = parseInt(line.split(/\s+/)[1])
if (totalKb && availableKb) break
}
if (!totalKb) return { pct: "?", used: "?", total: "?" }
const usedGb = ((totalKb - availableKb) / 1048576).toFixed(1)
const totalGb = (totalKb / 1048576).toFixed(0)
const pct = `${Math.round(((totalKb - availableKb) / totalKb) * 100)}`
return { pct, used: usedGb, total: totalGb }
}
function getGpuInfo(): string {
try {
const out = exec("bash -c \"nvidia-smi --query-gpu=utilization.gpu,temperature.gpu --format=csv,noheader,nounits 2>/dev/null\"")
const [usage, temp] = out.trim().split(", ")
return `GPU ${usage}% · ${temp}°C`
} catch {
return ""
}
}
export default function SystemStats() {
const cpu = createPoll("0", 3000, () => getCpuUsage())
const ram = createPoll("", 5000, () => {
const r = getRamUsage()
return JSON.stringify(r)
})
const ramLabel = ram((v: string) => {
try {
const r = JSON.parse(v)
return `RAM ${r.used}/${r.total}G`
} catch { return "RAM ?" }
})
const gpu = createPoll("", 5000, () => getGpuInfo())
return (
<box>
<label class="cpu module" label={cpu((v: string) => `CPU ${v}%`)} />
<label class="separator" label="│" />
<label class="ram module" label={ramLabel} />
<label class="separator" label="│" />
<label class="gpu module" label={gpu((v: string) => v || "GPU ?")} />
</box>
)
}

View File

@@ -0,0 +1,44 @@
import AstalWp from "gi://AstalWp"
import { createBinding } from "ags"
export default function Volume() {
const speaker = AstalWp.get_default()!.defaultSpeaker!
const volume = createBinding(speaker, "volume")
const mute = createBinding(speaker, "mute")
const text = volume((v: number) => {
const pct = Math.round(v * 100)
const muted = speaker.mute
let icon = "󰕾"
if (muted) icon = "󰝟"
else if (v > 0.66) icon = "󰕾"
else if (v > 0.33) icon = "󰖀"
else if (v > 0) icon = "󰕿"
else icon = "󰝟"
return `${icon} ${pct}%`
})
const cls = mute((m: boolean) =>
m ? "volume module muted" : "volume module"
)
return (
<button
class={cls}
onClicked={() => {
speaker.mute = !speaker.mute
}}
onScroll={(_self: any, event: any) => {
const delta = event.delta_y
if (delta < 0) {
speaker.volume = Math.min(speaker.volume + 0.05, 1.0)
} else {
speaker.volume = Math.max(speaker.volume - 0.05, 0.0)
}
}}
>
<label label={text} />
</button>
)
}

View File

@@ -0,0 +1,97 @@
import app from "ags/gtk3/app"
import { Astal, Gtk, Gdk } from "ags/gtk3"
import { createPoll } from "ags/time"
import { getBrainState } from "../../lib/brain"
import GLib from "gi://GLib"
function BrainContent() {
const state = createPoll("", 10000, () => JSON.stringify(getBrainState()))
const version = state((s: string) => {
try { return `kernel v${JSON.parse(s).kernelVersion}` } catch { return "" }
})
const focus = state((s: string) => {
try { return JSON.parse(s).focus } catch { return "..." }
})
const session = state((s: string) => {
try { return JSON.parse(s).session || "aucune session active" } catch { return "..." }
})
const intentionsTitle = state((s: string) => {
try {
const i = JSON.parse(s).intentions
return ` INTENTIONS (${i.active} active${i.active > 1 ? "s" : ""})`
} catch { return " INTENTIONS" }
})
const intentionsList = state((s: string) => {
try {
return JSON.parse(s).intentions.names.map((n: string) => `${n}`).join("\n") || " aucune"
} catch { return "..." }
})
const todosTitle = state((s: string) => {
try { const t = JSON.parse(s).todos; return `${t.open} ouverts / ${t.done} faits` } catch { return "" }
})
const todosList = state((s: string) => {
try {
return JSON.parse(s).todos.top3.map((item: string) => `${item}`).join("\n") || " rien"
} catch { return "..." }
})
return (
<box orientation={Gtk.Orientation.VERTICAL} class="brain-content">
<box class="brain-section">
<label class="brain-section-title" label=" FOCUS" xalign={0} />
<label class="brain-version" label={version} xalign={1} hexpand />
</box>
<label class="brain-focus" label={focus} xalign={0} wrap />
<box class="brain-divider" />
<box class="brain-section">
<label class="brain-section-title" label=" SESSION" xalign={0} />
</box>
<label class="brain-session" label={session} xalign={0} />
<box class="brain-divider" />
<box class="brain-section">
<label class="brain-section-title" label={intentionsTitle} xalign={0} />
</box>
<label class="brain-intentions-list" label={intentionsList} xalign={0} />
<box class="brain-divider" />
<box class="brain-section">
<label class="brain-section-title" label="󰄲 TODOS" xalign={0} />
<label class="brain-todos-count" label={todosTitle} xalign={1} hexpand />
</box>
<label class="brain-todos-list" label={todosList} xalign={0} />
</box>
)
}
export default function BrainPower(gdkmonitor: Gdk.Monitor) {
const { TOP, LEFT, BOTTOM } = Astal.WindowAnchor
return (
<window
class="BrainPower"
name="brain-power"
visible={false}
gdkmonitor={gdkmonitor}
exclusivity={Astal.Exclusivity.NONE}
anchor={TOP | LEFT | BOTTOM}
application={app}
layer={Astal.Layer.OVERLAY}
>
<box orientation={Gtk.Orientation.VERTICAL} class="brain-panel">
<box class="brain-header">
<label class="brain-title" label=" BRAIN POWER" hexpand xalign={0} />
<button
class="brain-close"
onClicked={() => {
const win = app.get_window("brain-power")
if (win) win.visible = false
}}
>
<label label="✕" />
</button>
</box>
<BrainContent />
</box>
</window>
)
}

View File

@@ -0,0 +1,6 @@
[Desktop Entry]
Type=Application
Name=Ghost Shell
Comment=violet-chaton v2 AGS statusbar
Exec=sh -c "ags run -d $HOME/.config/ags-v3 -g 3"
X-GNOME-Autostart-enabled=true

270
help.md
View File

@@ -1,4 +1,6 @@
# violet-chaton — référence des commandes # violet-chaton v2 — reference des commandes
> Tape `hotkeys` dans le terminal pour un rappel rapide.
--- ---
@@ -6,28 +8,33 @@
| Commande | Alias | Description | | Commande | Alias | Description |
|---|---|---| |---|---|---|
| `ls` | `eza --icons --git` | Listing coloré avec icônes | | `ls` | eza --icons | Listing colore avec icones |
| `ll` | `eza -l --git` | Listing long avec infos git | | `ll` | eza -l --git | Listing long avec infos git |
| `lt` | `eza --tree` | Arborescence en arbre | | `la` | eza -la --git | Listing complet (fichiers caches inclus) |
| `cd <dossier>` | zoxide | Navigation intelligente (mémorise les dossiers visités) | | `lt` | eza --tree | Arborescence en arbre (3 niveaux) |
| `cd <partiel>` | zoxide | Saute vers le dossier le plus probable — ex: `cd doc``~/Documents` | | `cd <dossier>` | zoxide | Navigation intelligente (memorise les dossiers visites) |
| `cd <partiel>` | zoxide | Saute vers le dossier le plus probable |
| `<nom_dossier>` | AUTO_CD | Entrer dans un dossier sans taper `cd` | | `<nom_dossier>` | AUTO_CD | Entrer dans un dossier sans taper `cd` |
| `yazi` | — | Explorateur de fichiers TUI (clavier) | | `y` | yazi + cd | Explorateur fichiers TUI — cd au dossier visite en quittant |
| `proj` | fzf ~/Dev | Project switcher — navigue dans tous les projets |
| `nemo` | — | Explorateur de fichiers GUI | | `nemo` | — | Explorateur de fichiers GUI |
| `fd <pattern>` | fdfind | Recherche de fichiers (remplace `find`) | | `fd <pattern>` | fdfind | Recherche de fichiers (remplace `find`) |
| `mkcd <nom>` | mkdir + cd | Creer un dossier et y entrer |
### yazi — raccourcis principaux ### yazi — raccourcis principaux
| Touche | Action | | Touche | Action |
|---|---| |---|---|
| `h/j/k/l` ou flèches | Navigation | | `h/j/k/l` ou fleches | Navigation |
| `Entrée` | Ouvrir / entrer dans le dossier | | `Entree` | Ouvrir / entrer dans le dossier |
| `Espace` | Sélectionner | | `Espace` | Selectionner |
| `y` | Copier | | `y` | Copier |
| `d` | Couper | | `d` | Couper |
| `p` | Coller | | `p` | Coller |
| `r` | Renommer | | `r` | Renommer |
| `D` | Supprimer | | `D` | Supprimer |
| `q` | Quitter | | `q` | Quitter (retour au dossier visite) |
> yazi utilise le protocole image kitty — les images s'affichent en preview native.
--- ---
@@ -35,12 +42,13 @@
| Commande | Alias | Description | | Commande | Alias | Description |
|---|---|---| |---|---|---|
| `cat <fichier>` | batcat | Affichage avec coloration syntaxique, sans pager | | `cat <fichier>` | batcat | Affichage avec coloration syntaxique (theme violet-chaton) |
| `bat <fichier>` | batcat | Comme cat avec numéros de lignes et pager | | `bat <fichier>` | batcat | Comme cat avec numeros de lignes et pager |
| `glow <fichier.md>` | — | Rendu Markdown dans le terminal | | `glow <fichier.md>` | — | Rendu Markdown dans le terminal |
| `man <commande>` | tldr | Pages de manuel simplifiées (remplace man) | | `tl <commande>` | tldr | Pages de manuel simplifiees avec exemples |
| `tldr <commande>` | — | Exemples pratiques d'une commande | | `man <commande>` | — | Pages de manuel completes (man reste intact) |
| `fetch` | — | Affiche les infos système avec le logo violet-chaton | | `fetch` | — | Infos systeme avec le logo violet-chaton |
| `colors` | — | Affiche la palette violet-chaton v2 complete |
--- ---
@@ -48,58 +56,120 @@
| Commande | Description | | Commande | Description |
|---|---| |---|---|
| `grep <pattern> <fichier>` | Recherche dans un fichier (--color=auto actif par défaut) | | `rg <pattern>` | Recherche dans les fichiers (ripgrep) |
| `rg <pattern>` | Recherche dans les fichiers (ripgrep, remplace grep) | | `fd <pattern>` | Recherche de fichiers |
| `fd <pattern>` | Recherche de fichiers (remplace find) | | `fzf` | Fuzzy finder interactif |
| `fzf` | Fuzzy finder interactif (pipe ou seul) |
### fzf — raccourcis clavier ### fzf — raccourcis clavier
| Touche | Action | | Touche | Action |
|---|---| |---|---|
| `Ctrl+G` | Recherche fuzzy d'un **fichier** dans `$HOME` (aperçu batcat) | | `Ctrl+G` | Recherche fuzzy d'un **fichier** (apercu bat) |
| `Ctrl+F` | Recherche fuzzy d'un **dossier** dans `$HOME` | | `Ctrl+F` | Recherche fuzzy d'un **dossier** |
| `Ctrl+R` | Recherche dans l'**historique** (via atuin) | | `Ctrl+R` | Recherche dans l'**historique** (via atuin) |
### fzf — commandes avancees
| Commande | Description |
|---|---|
| `glog` | Git log interactif — preview du commit, copie hash au presse-papier |
| `fkill` | Process killer — selectionne et tue un process |
--- ---
## Git ## Git
| Commande | Alias | Description | | Commande | Alias | Description |
|---|---|---| |---|---|---|
| `lg` | lazygit | Interface TUI complète pour git | | `lg` | lazygit | Interface TUI complete pour git (theme violet-chaton v2) |
| `git diff` | — | Affichage amélioré par git-delta (couleurs, side-by-side) | | `glog` | — | Git log fzf avec preview des commits |
| `gh pr create` | — | Créer une pull request depuis le terminal | | `git diff` | — | Diffs colores par delta (side-by-side, violet-chaton) |
| `gh issue list` | — | Lister les issues du dépôt courant |
| `gh auth login` | — | S'authentifier sur GitHub |
### lazygit — raccourcis principaux ### lazygit — raccourcis principaux
| Touche | Action | | Touche | Action |
|---|---| |---|---|
| `1/2/3/4/5` | Changer de panneau (status/branches/commits/stash/reflog) | | `1/2/3/4/5` | Changer de panneau |
| `Espace` | Stage / unstage un fichier | | `Espace` | Stage / unstage |
| `c` | Commit | | `c` | Commit |
| `p` | Push | | `p` | Push |
| `P` | Pull | | `P` | Pull |
| `b` | Gérer les branches |
| `z` | Undo |
| `q` | Quitter | | `q` | Quitter |
### git-delta — navigation dans les diffs ### git-delta — navigation
| Touche | Action | | Touche | Action |
|---|---| |---|---|
| `n` | Hunk suivant | | `n` | Hunk suivant |
| `N` | Hunk précédent | | `N` | Hunk precedent |
| `q` | Quitter | | `q` | Quitter |
--- ---
## Monitoring & système ## Terminal kitty — splits & layouts
| Touche | Action |
|---|---|
| `Ctrl+Shift+\` | Split vertical |
| `Ctrl+Shift+-` | Split horizontal |
| `Ctrl+Shift+Z` | Zoom — stack toggle (fullscreen le split actif) |
| `Ctrl+Shift+←→↑↓` | Naviguer entre splits |
| `Ctrl+Shift+F` | Deplacer split en avant |
| `Ctrl+Shift+B` | Deplacer split en arriere |
| `Ctrl+Shift+R` | Redimensionner split |
| `Ctrl+Shift+1` | Layout tall |
| `Ctrl+Shift+2` | Layout fat |
| `Ctrl+Shift+3` | Layout grid |
| `Ctrl+Shift+4` | Layout horizontal |
| `Ctrl+Shift+5` | Layout vertical |
| `Ctrl+Shift+T` | Nouvel onglet |
| `Ctrl+Shift+W` | Fermer onglet |
| `Ctrl+Shift+H/L` | Onglet precedent/suivant |
| `Ctrl+Shift+,` | Renommer onglet |
| `Ctrl+V` | Coller |
| `Ctrl+C` | Copier / interrompre |
| `Ctrl+Shift++/-/0` | Zoom police |
| `Ctrl+Shift+F5` | Recharger la config |
---
## Vi-mode zsh
Le shell demarre en **mode insert** (beam cursor). `Escape` passe en **mode normal** (block cursor).
### Mode normal (Escape)
| Touche | Action |
|---|---|
| `h/j/k/l` | Deplacement gauche/bas/haut/droite |
| `w` / `b` | Mot suivant / precedent |
| `0` / `$` | Debut / fin de ligne |
| `x` | Supprimer caractere |
| `dd` | Supprimer la ligne |
| `yy` | Copier la ligne |
| `p` | Coller |
| `i` | Retour mode insert |
| `a` | Insert apres le curseur |
| `A` | Insert en fin de ligne |
### Mode insert (keybindings preserves)
| Touche | Action |
|---|---|
| `Ctrl+A` | Debut de ligne |
| `Ctrl+E` | Fin de ligne |
| `Ctrl+W` | Supprimer mot precedent |
| `Ctrl+U` | Supprimer jusqu'au debut |
| `Ctrl+K` | Supprimer jusqu'a la fin |
| `Ctrl+Y` | Coller (yank) |
| `Ctrl+Space` | Accepter suggestion auto |
---
## Monitoring & systeme
| Commande | Alias | Description | | Commande | Alias | Description |
|---|---|---| |---|---|---|
| `btop` | — | Moniteur système interactif (CPU, RAM, réseau, disque) | | `btop` / `top` | — | Moniteur systeme (theme violet-chaton v2) |
| `disk` | ncdu | Analyse de l'espace disque interactif | | `disk` / `dust` | — | Analyse de l'espace disque |
| `ncdu` | — | Idem (nom original) | | `procs` | — | Process viewer moderne |
| `tokei` | — | Stats code par langage |
| `hyperfine <cmd>` | — | Benchmark d'une commande |
| `gping <host>` | — | Ping avec graphe temps reel |
--- ---
@@ -107,14 +177,9 @@
| Commande / Touche | Description | | Commande / Touche | Description |
|---|---| |---|---|
| `Ctrl+R` | Recherche dans l'historique avec atuin (fuzzy, filtré par dossier/host) | | `Ctrl+R` | Recherche fuzzy dans l'historique |
| `atuin search <terme>` | Recherche dans l'historique en ligne de commande | | `atuin stats` | Statistiques commandes |
| `atuin stats` | Statistiques sur les commandes les plus utilisées | | `atuin sync` | Synchroniser entre machines |
| `atuin sync` | Synchroniser l'historique entre machines (compte requis) |
| `atuin register` | Créer un compte atuin pour la synchronisation |
> L'interface atuin affiche : code de sortie | durée | timestamp | commande.
> L'historique est partagé entre sessions et machines si atuin est configuré avec un compte.
--- ---
@@ -122,12 +187,10 @@
| Commande / Touche | Description | | Commande / Touche | Description |
|---|---| |---|---|
| `fuck` | Corrige la dernière commande ratée (thefuck) | | `fuck` | Corrige la derniere commande ratee (thefuck) |
| `Ctrl+Space` | Accepte la suggestion automatique complète | | `Ctrl+Space` | Accepte la suggestion complete |
| `→` (flèche droite) | Accepte partiellement la suggestion (mot par mot) | | `→` (fleche droite) | Accepte partiellement (mot par mot) |
| `Tab` | Autocomplétion avec menu interactif | | `Tab` | Autocompletion avec menu interactif |
> zsh corrige aussi automatiquement les petites typos de commandes (option `CORRECT`).
--- ---
@@ -135,15 +198,13 @@
| Commande | Description | | Commande | Description |
|---|---| |---|---|
| `cava` | Visualiseur audio dans le terminal | | `cava` | Visualiseur audio (gradient Mitsuri) |
| `pipes` | Animation de tuyaux colorés | | `pipes` | Animation de tuyaux colores |
| `cbonsai` | Bonsaï animé dans le terminal | | `cbonsai` | Bonsai anime |
| `chafa <image>` | Affiche une image dans le terminal | | `chafa <image>` | Affiche une image dans le terminal |
| `lolcat` | Colorie la sortie d'une commande en arc-en-ciel (ex: `ls \| lolcat`) | | `lolcat` | Colorie en arc-en-ciel (ex: `ls \| lolcat`) |
| `cmatrix` | Pluie de caractères style Matrix | | `cmatrix` | Pluie de caracteres Matrix |
| `toilet -f big <texte>` | Affiche du texte en gros ASCII art coloré | | `toilet -f big <texte>` | Texte en gros ASCII art |
| `jp2a <image.jpg>` | Convertit une image en ASCII art dans le terminal |
| `w3m <url>` | Navigue sur le web en mode texte depuis le terminal |
--- ---
@@ -151,76 +212,41 @@
| Commande | Description | | Commande | Description |
|---|---| |---|---|
| `qalc` | Calculatrice interactive (unités, conversions, ex: `150 EUR to USD`) | | `sd <ancien> <nouveau>` | Remplacement simplifie (remplace sed) |
| `jq <filtre> <fichier.json>` | Traite et formate du JSON en ligne de commande | | `qalc` | Calculatrice interactive (unites, conversions) |
| `uv` | Gestionnaire de paquets Python ultra-rapide (remplace pip/venv) | | `jq` | Traite et formate du JSON |
| `uvx <outil>` | Exécute un outil Python dans un environnement isolé temporaire | | `weather [ville]` | Meteo rapide |
| `uv` / `uvx` | Gestionnaire Python ultra-rapide |
--- ---
## Cheat sheets interactifs ## Coloration syntaxique zsh
| Commande | Description |
|---|---|
| `navi` | Interface interactive de cheat sheets (chercher des exemples de commandes) |
| `tldr <commande>` | Résumé rapide d'une commande avec exemples |
---
## Prompt (starship)
Le prompt affiche automatiquement sur **2 lignes** :
**Ligne 1 :** OS | User@Host | Dossier courant | Branche git + état | Langage détecté + version | Durée dernière commande (si >2s) | RAM | Heure
**Ligne 2 :** Code de retour si erreur | Caractère `` (bleu ok / rouge erreur)
**État git affiché :**
| Symbole | Signification |
|---|---|
| `⇡N` | N commits en avance sur le remote |
| `⇣N` | N commits en retard |
| `?N` | N fichiers non trackés |
| `!N` | N fichiers modifiés |
| `+N` | N fichiers stagés |
| `✘N` | Conflits |
---
## Plugins zsh actifs
| Plugin | Effet |
|---|---|
| `zsh-autosuggestions` | Suggestions grises basées sur l'historique |
| `zsh-syntax-highlighting` | Coloration de la commande en cours de frappe |
| `zsh-completions` | Complétions supplémentaires pour de nombreux outils |
| `zinit` | Gestionnaire de plugins (chargement automatique au démarrage) |
---
## Coloration syntaxique du terminal
| Type | Couleur | | Type | Couleur |
|---|---| |---|---|
| Commandes | Cyan | | Commandes | Lavande `#a4b4ff` |
| Alias | Violet | | Alias | Lilac `#c9a0ff` |
| Builtins zsh | Rose | | Builtins | Magenta `#ff4da6` |
| Chaînes | Violet | | Chaines | Mitsuri `#9adba8` |
| Chemins | Blanc souligné | | Options `--flag` | Champagne `#e8c87a` |
| Erreurs / inconnu | Rouge gras | | Chemins | Text souligne |
| Commentaires | Gris italique | | Erreurs / inconnu | Danger `#f25c7a` bold |
| Globs (`*`, `?`) | Jaune | | Commentaires | Muted `#716686` italic |
| Redirections `> \|` | Magenta bold |
| Globs `* ?` | Champagne |
--- ---
## Options zsh actives ## Prompt starship (3 lignes)
| Option | Effet | ```
|---|---| ┌── [OS] [dossier] [git branch] [git status] [langages] ─── [duree] [batterie] [heure]
| `AUTO_CD` | Entrer dans un dossier sans taper `cd` | └─ [status]
| `CORRECT` | Correction automatique des typos | ```
| `GLOB_DOTS` | Les fichiers cachés `.xxx` inclus dans les globs |
| `SHARE_HISTORY` | Historique partagé entre toutes les sessions zsh | - **Ligne 1** : separateur + infos (remplissage pointille entre gauche/droite)
| `HIST_IGNORE_DUPS` | Ne pas enregistrer les doublons dans l'historique | - **Ligne 2** : prompt `` — vert mitsuri si OK, magenta si erreur
| `HIST_IGNORE_SPACE` | Les commandes précédées d'un espace ne sont pas enregistrées | - **username@host** : cache en local, apparait en SSH
| `NO_BEEP` | Silence — pas de bip | - **sudo** : indicateur discret quand actif
- **battery** : visible sous 70%, rouge sous 30%
- **brain\_name** : affiche en italic quand la variable est definie

View File

@@ -0,0 +1,68 @@
# violet-chaton v2 — Palette Reference
> Mitsuri Kanroji inspired gradient magenta → green
> Created by Tetardtek — 2026
## Colors
### Backgrounds
| Name | Hex | Usage |
|------|-----|-------|
| crust | `#1a0e27` | Deepest background, panels heavy glass |
| base | `#261537` | Primary background, bar glass |
| mantle | `#341c4a` | Elevated surfaces |
| surface0 | `#3d2454` | Input backgrounds, troughs |
| surface1 | `#493161` | Hover states, subtle borders |
| surface2 | `#5a3875` | Active states, dividers |
### Accents
| Name | Hex | Usage |
|------|-----|-------|
| magenta | `#ff4da6` | Primary accent — clock, battery, volume, borders, heartbeat |
| lilac | `#c9a0ff` | Secondary accent — hover states, media, brain panel |
| mitsuri | `#9adba8` | Green pastel — charging state, success, health good |
| lavande | `#a4b4ff` | Blue-violet — date, CPU, network, info states |
| champagne | `#e8c87a` | Warm gold — warnings, session info |
| danger | `#f25c7a` | Red-pink — errors, low battery, disconnected |
### Text
| Name | Hex | Usage |
|------|-----|-------|
| text | `#f0eaf8` | Primary text |
| subtext1 | `#c4b8d4` | Secondary text, todo items |
| subtext0 | `#9a8fad` | Tertiary text |
| muted | `#716686` | Disabled, placeholders, empty states |
## Font
- **Primary**: Maple Mono NF (cursive italics)
- **Size**: 13px bold (bar), 14px 900 (clock, prompt)
- **Icons**: Nerd Font glyphs from Maple Mono NF
## Signature Visual Elements
- Glassmorphism: `rgba(38, 21, 55, 0.88)` (base @ 88%)
- Heavy glass: `rgba(26, 14, 39, 0.94)` (crust @ 94%)
- Border accent: `3px solid rgba(255, 77, 166, 0.60)` (magenta @ 60%)
- Hover glow: `box-shadow: 0 4px 28px rgba(201, 160, 255, 0.18)` (lilac @ 18%)
- Gradient progress: `linear-gradient(to right, magenta, lilac, lavande, mitsuri)`
- Heartbeat: gradient `magenta 0% → magenta 60% → lilac 80% → magenta 60% → magenta 0%`
- Border radius: 14px (pills), 8px (buttons)
## SCSS Variables
```scss
$crust: #1a0e27;
$base: #261537;
$mantle: #341c4a;
$surface0: #3d2454;
$surface1: #493161;
$surface2: #5a3875;
$magenta: #ff4da6;
$lilac: #c9a0ff;
$mitsuri: #9adba8;
$lavande: #a4b4ff;
$champagne: #e8c87a;
$danger: #f25c7a;
$text: #f0eaf8;
$subtext1: #c4b8d4;
$subtext0: #9a8fad;
$muted: #716686;
```