diff --git a/MPRIS-CUSTOMIZATION.md b/MPRIS-CUSTOMIZATION.md
new file mode 100644
index 00000000..9afbe0bd
--- /dev/null
+++ b/MPRIS-CUSTOMIZATION.md
@@ -0,0 +1,227 @@
+# Ultra-Customizable MPRIS Widget for SwayNC
+
+## 📋 Table of Contents
+
+- [Overview](#overview)
+- [Configuration Options](#configuration-options)
+- [Practical Examples](#practical-examples)
+- [Modified Files](#modified-files)
+
+---
+
+## Overview
+
+This enhancement to SwayNotificationCenter adds **8 new JSON options** to fully customize the MPRIS widget, allowing layouts ranging from ultra-compact (only 3 buttons) to complete with all controls.
+
+## Configuration Options
+
+### 🎨 Element Visibility
+
+| Option | Type | Default | Description |
+|--------|------|---------|-------------|
+| `show-album-art` | string | `"always"` | Controls album art display
• `"always"` - Always visible
• `"when-available"` - Only if artwork exists
• `"never"` - Always hidden |
+| `show-title` | boolean | `true` | Shows track title or player name |
+| `show-subtitle` | boolean | `true` | Shows "Artist - Album" |
+| `show-background` | boolean | `true` | Displays blurred album art background |
+
+### 🎛️ Controls
+
+| Option | Type | Default | Description |
+|--------|------|---------|-------------|
+| `show-shuffle` | boolean | `true` | Shuffle button |
+| `show-repeat` | boolean | `true` | Repeat button (None/Playlist/Track) |
+| `show-favorite` | boolean | `true` | Favorite button _(not yet implemented in original code)_ |
+
+### 🧩 Layout
+
+| Option | Type | Default | Description |
+|--------|------|---------|-------------|
+| `compact-mode` | boolean | `false` | Optimized layout for small height _(placeholder for future optimizations)_ |
+| `button-size` | integer | `-1` | Button size in pixels
• `-1` - Default theme size |
+
+---
+
+## Practical Examples
+
+### 🎯 Ultra-Compact Mode (only play/pause/next/prev)
+
+```json
+{
+ "widget-config": {
+ "mpris": {
+ "show-album-art": "never",
+ "show-title": false,
+ "show-subtitle": false,
+ "show-background": false,
+ "show-shuffle": false,
+ "show-repeat": false,
+ "compact-mode": true
+ }
+ }
+}
+```
+
+**Result:** Only 3 horizontal buttons (⏮️ ⏯️ ⏭️) + player title
+
+---
+
+### 🎧 Minimalist Mode (cover + basic controls)
+
+```json
+{
+ "widget-config": {
+ "mpris": {
+ "show-album-art": "when-available",
+ "show-title": true,
+ "show-subtitle": false,
+ "show-background": true,
+ "show-shuffle": false,
+ "show-repeat": false
+ }
+ }
+}
+```
+
+**Result:** Album cover + title + 3 main buttons
+
+---
+
+### 🎹 Complete Mode (all controls)
+
+```json
+{
+ "widget-config": {
+ "mpris": {
+ "show-album-art": "always",
+ "show-title": true,
+ "show-subtitle": true,
+ "show-background": true,
+ "show-shuffle": true,
+ "show-repeat": true,
+ "button-size": 36
+ }
+ }
+}
+```
+
+**Result:** Complete interface with all elements
+
+---
+
+### 🚫 Filter specific players
+
+```json
+{
+ "widget-config": {
+ "mpris": {
+ "blacklist": ["firefox", "chromium", "spotify"],
+ "show-shuffle": false,
+ "show-repeat": false
+ }
+ }
+}
+```
+
+**Result:** Ignores browsers and Spotify, without shuffle/repeat buttons
+
+---
+
+## Modified Files
+
+### 1. `src/controlCenter/widgets/mpris/mpris.vala`
+
+- **Struct `Config`** expanded with 8 new fields
+- **Parsing** of new JSON options in constructor
+
+### 2. `src/controlCenter/widgets/mpris/mpris_player.vala`
+
+- **`update_title()`** - Respects `show_title`
+- **`update_sub_title()`** - Respects `show_subtitle`
+- **`update_album_art()`** - Respects `show_background`
+- **`update_button_shuffle()`** - Early return if `!show_shuffle`
+- **`update_button_repeat()`** - Early return if `!show_repeat`
+
+### 3. `src/configSchema.json`
+
+- Added 8 new properties to `mpris` widget schema
+
+### 4. `data/ui/mpris_player.blp`
+
+- Reverted to original behavior (code-controlled)
+
+---
+
+## Building
+
+```bash
+cd ~/git-clones/SwayNotificationCenter
+meson setup build --prefix=/usr --wipe
+meson compile -C build
+sudo meson install -C build
+killall swaync && swaync &
+```
+
+---
+
+## User Configuration
+
+Edit `~/.config/swaync/config.json` (create if it doesn't exist):
+
+```json
+{
+ "$schema": "/etc/xdg/swaync/configSchema.json",
+ "widgets": ["mpris", "notifications"],
+ "widget-config": {
+ "mpris": {
+ "show-album-art": "never",
+ "show-title": false,
+ "show-subtitle": false,
+ "show-shuffle": false,
+ "show-repeat": false
+ }
+ }
+}
+```
+
+Then reload the config:
+
+```bash
+swaync-client --reload-config
+```
+
+---
+
+## Feature Status
+
+| Feature | Status |
+|---------------|--------|
+| ✅ `show-album-art` | **Funcional** |
+| ✅ `show-title` | **Funcional** |
+| ✅ `show-subtitle` | **Funcional** |
+| ✅ `show-background` | **Funcional** |
+| ✅ `show-shuffle` | **Funcional** |
+| ✅ `show-repeat` | **Funcional** |
+| ⚠️ `show-favorite` | **Placeholder** (botão não existe no código original) |
+| ⚠️ `compact-mode` | **Placeholder** (requer modificações no layout do .blp) |
+| ⚠️ `button-size` | **Placeholder** (requer aplicação dinâmica de pixel-size) |
+
+---
+
+## Próximos Passos (Opcional)
+
+1. **Implementar `button-size`**: Aplicar `set_pixel_size()` nos botões dinamicamente
+2. **Implementar `compact-mode`**: Criar layout alternativo no `.blp` com orientação horizontal
+3. **Adicionar `show-favorite`**: Criar botão de favorito/coração se o player suportar
+
+---
+
+## Créditos
+
+- **SwayNotificationCenter Original**: [ErikReider/SwayNotificationCenter](https://github.com/ErikReider/SwayNotificationCenter)
+- **Modificações**: Customização MPRIS ultra-personalizável via JSON
+
+---
+
+## Licença
+
+Mesma licença do projeto original (GPL-3.0).
diff --git a/TESTING-GUIDE.md b/TESTING-GUIDE.md
new file mode 100644
index 00000000..d18fa4ca
--- /dev/null
+++ b/TESTING-GUIDE.md
@@ -0,0 +1,221 @@
+# 🎵 Quick Guide: Testing MPRIS Configurations
+
+## Current Status
+
+✅ **SwayNC compiled and installed** with new MPRIS options
+✅ **Config loading**: `/home/bitter/.config/swaync/config.json`
+✅ **MPRIS working**: Chromium player detected
+
+## Current Configuration
+
+You have `"show-album-art": "never"` on line 140 of your config.
+
+## How to Test the New Options
+
+### Method 1: Interactive Script (Recommended)
+
+```bash
+cd ~/git-clones/SwayNotificationCenter
+./test-mpris-configs.sh
+```
+
+The script offers 4 presets:
+
+- **Ultra-Compact**: Only control buttons
+- **Minimal**: Cover + title + controls
+- **Complete**: All elements
+- **No-Art**: No images but with shuffle/repeat
+
+### Method 2: Manual Editing
+
+Edit `~/.config/swaync/config.json` and add options to the `mpris` section:
+
+```json
+"mpris": {
+ "show-album-art": "when-available",
+ "show-title": true,
+ "show-subtitle": true,
+ "show-background": true,
+ "show-shuffle": false,
+ "show-repeat": false,
+ "show-favorite": false,
+ "compact-mode": false,
+ "button-size": -1,
+ "autohide": false,
+ "loop-carousel": true
+}
+```
+
+Then reload:
+
+```bash
+swaync-client --reload-config
+```
+
+### Method 3: Quick Commands
+
+**Ultra-Compact (only 3 buttons):**
+
+```bash
+cat > /tmp/mpris_patch.json << 'EOF'
+{
+ "show-album-art": "never",
+ "show-title": false,
+ "show-subtitle": false,
+ "show-shuffle": false,
+ "show-repeat": false
+}
+EOF
+
+jq '.["widget-config"]["mpris"] = input' \
+ ~/.config/swaync/config.json /tmp/mpris_patch.json > /tmp/config_new.json
+mv /tmp/config_new.json ~/.config/swaync/config.json
+swaync-client --reload-config
+```
+
+**Modo Completo:**
+
+```bash
+jq '.["widget-config"]["mpris"] = {
+ "show-album-art": "always",
+ "show-title": true,
+ "show-subtitle": true,
+ "show-background": true,
+ "show-shuffle": true,
+ "show-repeat": true
+}' ~/.config/swaync/config.json > /tmp/config_new.json
+mv /tmp/config_new.json ~/.config/swaync/config.json
+swaync-client --reload-config
+```
+
+## Testing with Players
+
+Play music in any MPRIS player:
+
+```bash
+# Spotify
+spotify &
+
+# VLC
+vlc ~/Música/exemplo.mp3 &
+
+# Chromium/Chrome (YouTube Music)
+chromium --app=https://music.youtube.com &
+
+# Firefox
+firefox https://soundcloud.com &
+```
+
+Then open the Control Center:
+
+```bash
+swaync-client -t -sw
+```
+
+## Available Options
+
+| Option | Values | Description |
+|-------|---------|-----------|
+| `show-album-art` | `"always"`, `"when-available"`, `"never"` | Controls album art |
+| `show-title` | `true`, `false` | Track title |
+| `show-subtitle` | `true`, `false` | Artist - Album |
+| `show-background` | `true`, `false` | Blurred background |
+| `show-shuffle` | `true`, `false` | Shuffle button |
+| `show-repeat` | `true`, `false` | Repeat button |
+| `show-favorite` | `true`, `false` | Favorite button (placeholder) |
+| `compact-mode` | `true`, `false` | Compact layout (placeholder) |
+| `button-size` | `-1` or pixels | Button size |
+| `autohide` | `true`, `false` | Hide when no media |
+| `loop-carousel` | `true`, `false` | Loop carousel |
+
+## Configuration Examples
+
+### For Desktop (space available)
+
+```json
+"mpris": {
+ "show-album-art": "always",
+ "show-title": true,
+ "show-subtitle": true,
+ "show-background": true,
+ "show-shuffle": true,
+ "show-repeat": true
+}
+```
+
+### For Laptop (space saving)
+
+```json
+"mpris": {
+ "show-album-art": "when-available",
+ "show-title": true,
+ "show-subtitle": false,
+ "show-shuffle": false,
+ "show-repeat": false
+}
+```
+
+### Extreme Minimalist Mode
+
+```json
+"mpris": {
+ "show-album-art": "never",
+ "show-title": false,
+ "show-subtitle": false,
+ "show-background": false,
+ "show-shuffle": false,
+ "show-repeat": false
+}
+```
+
+## Troubleshooting
+
+**Config not being applied?**
+
+```bash
+# Check if SwayNC is running
+ps aux | grep swaync
+
+# Restart completely
+killall swaync
+swaync &
+```
+
+**JSON error?**
+
+```bash
+# Validate syntax
+jq . ~/.config/swaync/config.json
+
+# Check schema
+jsonschema -i ~/.config/swaync/config.json /etc/xdg/swaync/configSchema.json
+```
+
+**Detailed logs:**
+
+```bash
+# Stop SwayNC
+killall swaync
+
+# Start with debug
+G_MESSAGES_DEBUG=all swaync
+```
+
+## Resultado Esperado
+
+Com `show-shuffle: false` e `show-repeat: false`, você deve ver:
+
+- Apenas os botões: **⏮️ Anterior** | **⏯️ Play/Pause** | **⏭️ Próximo**
+- Sem botões de shuffle (🔀) e repeat (🔁)
+
+Se `show-album-art: "never"`:
+
+- Nenhuma imagem/ícone será exibido
+
+## Próximos Passos
+
+Para personalizar ainda mais, consulte:
+
+- `MPRIS-CUSTOMIZATION.md` - Documentação completa
+- `mpris-config-example.json` - Template com todas as opções
+- `/etc/xdg/swaync/configSchema.json` - Schema completo
diff --git a/mpris-config-example.json b/mpris-config-example.json
new file mode 100644
index 00000000..152ca3a1
--- /dev/null
+++ b/mpris-config-example.json
@@ -0,0 +1,43 @@
+{
+ "$schema": "/etc/xdg/swaync/configSchema.json",
+ "positionX": "right",
+ "positionY": "top",
+ "control-center-width": 500,
+ "control-center-height": 600,
+ "control-center-margin-top": 10,
+ "control-center-margin-right": 10,
+ "fit-to-screen": false,
+
+ "widgets": ["title", "dnd", "mpris", "notifications"],
+
+ "widget-config": {
+ "title": {
+ "text": "Notificações",
+ "clear-all-button": true,
+ "button-text": "Limpar"
+ },
+
+ "dnd": {
+ "text": "Não Perturbar"
+ },
+
+ "mpris": {
+ "autohide": false,
+ "loop-carousel": true,
+
+ "show-album-art": "when-available",
+ "show-title": true,
+ "show-subtitle": true,
+ "show-background": true,
+
+ "show-shuffle": false,
+ "show-repeat": false,
+ "show-favorite": false,
+
+ "compact-mode": true,
+ "button-size": 32,
+
+ "blacklist": ["firefox", "chromium"]
+ }
+ }
+}
diff --git a/src/configSchema.json b/src/configSchema.json
index c6924a14..a7b1ce40 100644
--- a/src/configSchema.json
+++ b/src/configSchema.json
@@ -1,795 +1,832 @@
{
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "title": "SwayNotificationCenter JSON schema",
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "$schema": {
- "type": "string",
- "description": "Pointer to the schema against which this document should be validated."
- },
- "ignore-gtk-theme": {
- "type": "boolean",
- "description": "Unsets the GTK_THEME environment variable, fixing a lot of issues with GTK themes ruining the users custom CSS themes.",
- "default": true
- },
- "positionX": {
- "type": "string",
- "description": "Horizontal position of control center and notification window",
- "default": "right",
- "enum": ["right", "left", "center"]
- },
- "layer": {
- "type": "string",
- "description": "Layer of notification window",
- "default": "overlay",
- "enum": ["background", "bottom", "top", "overlay"]
- },
- "layer-shell": {
- "type": "boolean",
- "description": "Whether or not the windows should be opened as layer-shell surfaces. Note: Requires swaync restart to apply",
- "default": true
- },
- "layer-shell-cover-screen": {
- "type": "boolean",
- "description": "Whether or not the windows should cover the whole screen when layer-shell is used. May exceed the height of the monitor if the value is too large. Fixes animations in compositors like Hyprland.",
- "default": true
- },
- "cssPriority": {
- "type": "string",
- "description": "Which GTK priority to use when loading the default and user CSS files. Pick \"user\" to override XDG_CONFIG_HOME/gtk-4.0/gtk.css",
- "default": "highest",
- "enum": ["application", "user"]
- },
- "positionY": {
- "type": "string",
- "description": "Vertical position of control center and notification window",
- "default": "top",
- "enum": ["top", "center", "bottom"]
- },
- "control-center-positionX": {
- "type": "string",
- "description": "Optional: Horizontal position of the control center. Supersedes positionX if not set to `none`",
- "default": "none",
- "enum": ["right", "left", "center", "none"]
- },
- "control-center-positionY": {
- "type": "string",
- "description": "Optional: Vertical position of the control center. Supersedes positionY if not set to `none`",
- "default": "none",
- "enum": ["top", "bottom", "none"]
- },
- "control-center-margin-top": {
- "type": "integer",
- "description": "The margin (in pixels) at the top of the control center. 0 to disable",
- "default": 0
- },
- "control-center-margin-bottom": {
- "type": "integer",
- "description": "The margin (in pixels) at the bottom of the control center. 0 to disable",
- "default": 0
- },
- "control-center-margin-right": {
- "type": "integer",
- "description": "The margin (in pixels) at the right of the control center. 0 to disable",
- "default": 0
- },
- "control-center-margin-left": {
- "type": "integer",
- "description": "The margin (in pixels) at the left of the control center. 0 to disable",
- "default": 0
- },
- "control-center-layer": {
- "type": "string",
- "description": "Layer of control center window",
- "default": "none",
- "enum": ["background", "bottom", "top", "overlay", "none"]
- },
- "control-center-exclusive-zone": {
- "type": "boolean",
- "description": "Whether or not the control center should follow the compositors exclusive zones. An example would be setting it to \"false\" to cover your panel/dock.",
- "default": true
- },
- "notification-2fa-action": {
- "type": "boolean",
- "description": "If each notification should display a 'COPY \"1234\"' action",
- "default": true
- },
- "notification-inline-replies": {
- "type": "boolean",
- "description": "If notifications should display a text field to reply if the sender requests it. NOTE: Replying in popup notifications is only available if the compositor supports GTK Layer-Shell ON_DEMAND keyboard interactivity.",
- "default": false
- },
- "notification-icon-size": {
- "type": "integer",
- "deprecated": true,
- "description": "deprecated (change the CSS root variable \"--notification-icon-size\"): The notification icon size (in pixels). The app icon size is 1/3",
- "default": -1
- },
- "notification-body-image-height": {
- "type": "integer",
- "description": "The notification body image height (in pixels)",
- "default": 100,
- "minimum": 100
- },
- "notification-body-image-width": {
- "type": "integer",
- "description": "The notification body image width (in pixels)",
- "default": 200,
- "minimum": 200
- },
- "timeout": {
- "type": "integer",
- "description": "The notification timeout for notifications with normal priority",
- "default": 10
- },
- "timeout-low": {
- "type": "integer",
- "description": "The notification timeout for notifications with low priority",
- "default": 5
- },
- "timeout-critical": {
- "type": "integer",
- "description": "The notification timeout for notifications with critical priority. 0 to disable",
- "default": 0
- },
- "notification-window-width": {
- "type": "integer",
- "description": "Width of the notification in pixels",
- "default": 500
- },
- "notification-window-height": {
- "type": "integer",
- "description": "Max height of the notification in pixels. -1 to use the full amount of space given by the compositor.",
- "default": -1
- },
- "notification-window-preferred-output": {
- "type": "string",
- "description": "The preferred output to open the notification window (popup notifications). Can either be the monitor connector name (ex: \"DP-1\"), or the full name, manufacturer model serial (ex: \"Acer Technologies XV272U V 503023B314202\"). If the output is not found, the currently focused one is picked"
- },
- "fit-to-screen": {
- "type": "boolean",
- "description": "If the control center should expand to both edges of the screen",
- "default": true
- },
- "relative-timestamps": {
- "type": "boolean",
- "description": "Display notification timestamps relative to now e.g. \"26 minutes ago\". If false, a local iso8601-formatted absolute timestamp is displayed.",
- "default": true
- },
- "control-center-height": {
- "type": "integer",
- "description": "Height of the control center in pixels. A value of -1 means that it will fit to the content. Ignored when 'fit-to-screen' is set to 'true'. Also limited to the height of the monitor, unless 'layer-shell-cover-screen' is set to false.",
- "default": 500,
- "minimum": -1
- },
- "control-center-width": {
- "type": "integer",
- "description": "Width of the control center in pixels",
- "default": 500,
- "minimum": 300
- },
- "control-center-preferred-output": {
- "type": "string",
- "description": "The preferred output to open the control center. Can either be the monitor connector name (ex: \"DP-1\"), or the full name, manufacturer model serial (ex: \"Acer Technologies XV272U V 503023B314202\"). If the output is not found, the currently focused one is picked"
- },
- "keyboard-shortcuts": {
- "type": "boolean",
- "description": "If control center should use keyboard shortcuts",
- "default": true
- },
- "notification-grouping": {
- "type": "boolean",
- "description": "If notifications should be grouped by app name",
- "default": true
- },
- "image-visibility": {
- "type": "string",
- "description": "The notification image visibility when no icon is available.",
- "default": "when-available",
- "enum": ["always", "when-available", "never"]
- },
- "transition-time": {
- "type": "integer",
- "description": "The notification animation duration. 0 to disable",
- "default": 200
- },
- "hide-on-clear": {
- "type": "boolean",
- "description": "Hides the control center after pressing \"Clear All\"",
- "default": false
- },
- "hide-on-action": {
- "type": "boolean",
- "description": "Hides the control center when clicking on notification action",
- "default": true
- },
- "text-empty": {
- "type": "string",
- "description": "Text that appears when there are no notifications to show",
- "default": "No Notifications"
- },
- "script-fail-notify": {
- "type": "boolean",
- "description": "Sends a notification if a script fails to run",
- "default": true
- },
- "scripts": {
- "type": "object",
- "description": "Which scripts to check and potentially run for every notification. If the notification doesn't include one of the properties, that property will be ignored. All properties (except for exec) use regex. If all properties match the given notification, the script will be run. Only the first matching script will be run.",
- "minProperties": 1,
- "additionalProperties": false,
- "patternProperties": {
- "^.{1,}$": {
- "type": "object",
- "description": "Your script object.",
- "required": ["exec"],
- "minProperties": 2,
- "additionalProperties": false,
- "properties": {
- "exec": {
- "type": "string",
- "description": "The script to run. Can also run regular shell commands."
- },
- "app-name": {
- "type": "string",
- "description": "The app-name. Uses Regex."
- },
- "desktop-entry": {
- "type": "string",
- "description": "The desktop-entry. Uses Regex."
- },
- "summary": {
- "type": "string",
- "description": "The summary of the notification. Uses Regex."
- },
- "body": {
- "type": "string",
- "description": "The body of the notification. Uses Regex."
- },
- "urgency": {
- "type": "string",
- "description": "The urgency of the notification.",
- "default": "Normal",
- "enum": ["Low", "Normal", "Critical"]
- },
- "category": {
- "type": "string",
- "description": "Which category the notification belongs to. Uses Regex."
- },
- "sound-file": {
- "type": "string",
- "description": "Which sound file the notification requested. Uses Regex."
- },
- "sound-name": {
- "type": "string",
- "description": "Which sound name the notification requested. Uses Regex."
- },
- "run-on": {
- "type": "string",
- "description": "Whether to run the script on an action being activated, or when the notification is received.",
- "enum": ["action", "receive"],
- "default": "receive"
- }
- }
- }
- }
- },
- "notification-action-filter": {
- "type": "object",
- "description": "Hides matching action(s) of matching notifications. If the notification doesn't include one of the properties, that property will be ignored. If all properties match the given notification, the matching actions will be hidden.",
- "minProperties": 1,
- "additionalProperties": false,
- "patternProperties": {
- "^.{1,}$": {
- "type": "object",
- "description": "Your script object.",
- "minProperties": 1,
- "anyOf": [
- { "required": ["id-matcher"] },
- { "required": ["text-matcher"] }
- ],
- "additionalProperties": false,
- "properties": {
- "use-regex": {
- "type": "boolean",
- "default": false,
- "description": "Indicates if all the below fields should use regex or not."
- },
- "app-name": {
- "type": "string",
- "description": "The app-name."
- },
- "desktop-entry": {
- "type": "string",
- "description": "The desktop-entry."
- },
- "id-matcher": {
- "type": "string",
- "description": "Matches the action identifier. Can be found by reading the output of swaync when run with the `G_MESSAGES_DEBUG=all` environment variable."
- },
- "text-matcher": {
- "type": "string",
- "description": "Matches the actions text visible in the notification."
- }
- }
- }
- }
- },
- "notification-visibility": {
- "type": "object",
- "description": "Set the visibility of each incoming notification. If the notification doesn't include one of the properties, that property will be ignored. All properties (except for state) use regex. If all properties match the given notification, the notification will be follow the provided state. Only the first matching object will be used.",
- "minProperties": 1,
- "additionalProperties": false,
- "patternProperties": {
- "^.{1,}$": {
- "type": "object",
- "description": "Your script object.",
- "required": ["state"],
- "minProperties": 2,
- "additionalProperties": false,
- "properties": {
- "state": {
- "type": "string",
- "description": "The notification visibility state.",
- "default": "enabled",
- "enum": ["ignored", "muted", "enabled", "transient"]
- },
- "app-name": {
- "type": "string",
- "description": "The app-name. Uses Regex."
- },
- "desktop-entry": {
- "type": "string",
- "description": "The desktop-entry. Uses Regex."
- },
- "summary": {
- "type": "string",
- "description": "The summary of the notification. Uses Regex."
- },
- "body": {
- "type": "string",
- "description": "The body of the notification. Uses Regex."
- },
- "urgency": {
- "type": "string",
- "description": "The urgency of the notification.",
- "default": "Normal",
- "enum": ["Low", "Normal", "Critical"]
- },
- "override-urgency": {
- "type": "string",
- "description": "The new urgency of the notification (optional)",
- "default": "unset",
- "enum": ["unset", "low", "normal", "critical"]
- },
- "category": {
- "type": "string",
- "description": "Which category the notification belongs to. Uses Regex."
- }
- }
- }
- }
- },
- "widgets": {
- "type": "array",
- "uniqueItems": true,
- "description": "Which order and which widgets to display. If the \"notifications\" widget isn't specified, it will be placed at the bottom.",
- "default": ["inhibitors", "title", "dnd", "notifications"],
- "items": {
- "type": "string",
- "$comment": "Sadly can't use regex and enums at the same time. Fix in the future?",
- "pattern": "^[a-zA-Z0-9_-]{1,}(#[a-zA-Z0-9_-]{1,}){0,1}?$"
- }
- },
- "widget-config": {
- "type": "object",
- "description": "Configure specific widget properties.",
- "additionalProperties": false,
- "$comment": "New widgets go here in \"patternProperties\" ↓",
- "patternProperties": {
- "notifications": {
- "$comment": "References the widget structure from \"widgets\" below",
- "$ref": "#/widgets/notifications"
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "SwayNotificationCenter JSON schema",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "$schema": {
+ "type": "string",
+ "description": "Pointer to the schema against which this document should be validated."
},
- "^title(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
- "$comment": "References the widget structure from \"widgets\" below",
- "$ref": "#/widgets/title"
+ "ignore-gtk-theme": {
+ "type": "boolean",
+ "description": "Unsets the GTK_THEME environment variable, fixing a lot of issues with GTK themes ruining the users custom CSS themes.",
+ "default": true
+ },
+ "positionX": {
+ "type": "string",
+ "description": "Horizontal position of control center and notification window",
+ "default": "right",
+ "enum": ["right", "left", "center"]
},
- "^dnd(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
- "$ref": "#/widgets/dnd"
+ "layer": {
+ "type": "string",
+ "description": "Layer of notification window",
+ "default": "overlay",
+ "enum": ["background", "bottom", "top", "overlay"]
},
- "^label(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
- "$ref": "#/widgets/label"
+ "layer-shell": {
+ "type": "boolean",
+ "description": "Whether or not the windows should be opened as layer-shell surfaces. Note: Requires swaync restart to apply",
+ "default": true
},
- "^mpris(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
- "$ref": "#/widgets/mpris"
+ "layer-shell-cover-screen": {
+ "type": "boolean",
+ "description": "Whether or not the windows should cover the whole screen when layer-shell is used. May exceed the height of the monitor if the value is too large. Fixes animations in compositors like Hyprland.",
+ "default": true
},
- "^buttons-grid(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
- "$ref": "#/widgets/buttons-grid"
+ "cssPriority": {
+ "type": "string",
+ "description": "Which GTK priority to use when loading the default and user CSS files. Pick \"user\" to override XDG_CONFIG_HOME/gtk-4.0/gtk.css",
+ "default": "highest",
+ "enum": ["application", "user"]
},
- "^menubar(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
- "$ref": "#/widgets/menubar"
+ "positionY": {
+ "type": "string",
+ "description": "Vertical position of control center and notification window",
+ "default": "top",
+ "enum": ["top", "center", "bottom"]
},
- "^slider(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
- "$ref": "#/widgets/slider"
+ "control-center-positionX": {
+ "type": "string",
+ "description": "Optional: Horizontal position of the control center. Supersedes positionX if not set to `none`",
+ "default": "none",
+ "enum": ["right", "left", "center", "none"]
},
- "^volume(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
- "$ref": "#/widgets/volume"
+ "control-center-positionY": {
+ "type": "string",
+ "description": "Optional: Vertical position of the control center. Supersedes positionY if not set to `none`",
+ "default": "none",
+ "enum": ["top", "bottom", "none"]
+ },
+ "control-center-margin-top": {
+ "type": "integer",
+ "description": "The margin (in pixels) at the top of the control center. 0 to disable",
+ "default": 0
+ },
+ "control-center-margin-bottom": {
+ "type": "integer",
+ "description": "The margin (in pixels) at the bottom of the control center. 0 to disable",
+ "default": 0
+ },
+ "control-center-margin-right": {
+ "type": "integer",
+ "description": "The margin (in pixels) at the right of the control center. 0 to disable",
+ "default": 0
+ },
+ "control-center-margin-left": {
+ "type": "integer",
+ "description": "The margin (in pixels) at the left of the control center. 0 to disable",
+ "default": 0
+ },
+ "control-center-layer": {
+ "type": "string",
+ "description": "Layer of control center window",
+ "default": "none",
+ "enum": ["background", "bottom", "top", "overlay", "none"]
+ },
+ "control-center-exclusive-zone": {
+ "type": "boolean",
+ "description": "Whether or not the control center should follow the compositors exclusive zones. An example would be setting it to \"false\" to cover your panel/dock.",
+ "default": true
+ },
+ "notification-2fa-action": {
+ "type": "boolean",
+ "description": "If each notification should display a 'COPY \"1234\"' action",
+ "default": true
+ },
+ "notification-inline-replies": {
+ "type": "boolean",
+ "description": "If notifications should display a text field to reply if the sender requests it. NOTE: Replying in popup notifications is only available if the compositor supports GTK Layer-Shell ON_DEMAND keyboard interactivity.",
+ "default": false
+ },
+ "notification-icon-size": {
+ "type": "integer",
+ "deprecated": true,
+ "description": "deprecated (change the CSS root variable \"--notification-icon-size\"): The notification icon size (in pixels). The app icon size is 1/3",
+ "default": -1
+ },
+ "notification-body-image-height": {
+ "type": "integer",
+ "description": "The notification body image height (in pixels)",
+ "default": 100,
+ "minimum": 100
+ },
+ "notification-body-image-width": {
+ "type": "integer",
+ "description": "The notification body image width (in pixels)",
+ "default": 200,
+ "minimum": 200
+ },
+ "timeout": {
+ "type": "integer",
+ "description": "The notification timeout for notifications with normal priority",
+ "default": 10
+ },
+ "timeout-low": {
+ "type": "integer",
+ "description": "The notification timeout for notifications with low priority",
+ "default": 5
+ },
+ "timeout-critical": {
+ "type": "integer",
+ "description": "The notification timeout for notifications with critical priority. 0 to disable",
+ "default": 0
+ },
+ "notification-window-width": {
+ "type": "integer",
+ "description": "Width of the notification in pixels",
+ "default": 500
+ },
+ "notification-window-height": {
+ "type": "integer",
+ "description": "Max height of the notification in pixels. -1 to use the full amount of space given by the compositor.",
+ "default": -1
+ },
+ "notification-window-preferred-output": {
+ "type": "string",
+ "description": "The preferred output to open the notification window (popup notifications). Can either be the monitor connector name (ex: \"DP-1\"), or the full name, manufacturer model serial (ex: \"Acer Technologies XV272U V 503023B314202\"). If the output is not found, the currently focused one is picked"
+ },
+ "fit-to-screen": {
+ "type": "boolean",
+ "description": "If the control center should expand to both edges of the screen",
+ "default": true
+ },
+ "relative-timestamps": {
+ "type": "boolean",
+ "description": "Display notification timestamps relative to now e.g. \"26 minutes ago\". If false, a local iso8601-formatted absolute timestamp is displayed.",
+ "default": true
+ },
+ "control-center-height": {
+ "type": "integer",
+ "description": "Height of the control center in pixels. A value of -1 means that it will fit to the content. Ignored when 'fit-to-screen' is set to 'true'. Also limited to the height of the monitor, unless 'layer-shell-cover-screen' is set to false.",
+ "default": 500,
+ "minimum": -1
+ },
+ "control-center-width": {
+ "type": "integer",
+ "description": "Width of the control center in pixels",
+ "default": 500,
+ "minimum": 300
+ },
+ "control-center-preferred-output": {
+ "type": "string",
+ "description": "The preferred output to open the control center. Can either be the monitor connector name (ex: \"DP-1\"), or the full name, manufacturer model serial (ex: \"Acer Technologies XV272U V 503023B314202\"). If the output is not found, the currently focused one is picked"
},
- "^backlight(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
- "$ref": "#/widgets/backlight"
+ "keyboard-shortcuts": {
+ "type": "boolean",
+ "description": "If control center should use keyboard shortcuts",
+ "default": true
},
- "^inhibitors(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
- "$comment": "References the widget structure from \"widgets\" below",
- "$ref": "#/widgets/inhibitors"
- }
- }
- }
- },
- "widgets": {
- "$comment": "New widgets go here",
- "notifications": {
- "type": "object",
- "description": "Control Center Title Widget",
- "additionalProperties": false,
- "properties": {
- "vexpand": {
- "type": "boolean",
- "description": "Whether or not the notifications widget should vertically expand or not",
- "default": true
- }
- }
- },
- "title": {
- "type": "object",
- "description": "Control Center Title Widget",
- "additionalProperties": false,
- "properties": {
- "text": {
- "type": "string",
- "description": "The title of the widget",
- "default": "Notifications"
- },
- "clear-all-button": {
- "type": "boolean",
- "description": "Whether to display a \"Clear All\" button",
- "default": true
- },
- "button-text": {
- "type": "string",
- "description": "\"Clear All\" button text",
- "default": "Clear All"
- }
- }
- },
- "dnd": {
- "type": "object",
- "description": "Control Center Do Not Disturb Widget",
- "additionalProperties": false,
- "properties": {
- "text": {
- "type": "string",
- "description": "The title of the widget",
- "default": "Do Not Disturb"
- }
- }
- },
- "label": {
- "type": "object",
- "description": "A generic widget that allows the user to add custom text",
- "additionalProperties": false,
- "properties": {
- "text": {
- "type": "string",
- "description": "The text content of the widget",
- "default": "Label Text"
- },
- "max-lines": {
- "type": "integer",
- "description": "The maximum lines",
- "default": 5
- }
- }
- },
- "mpris": {
- "type": "object",
- "description": "A widget that displays multiple music players",
- "additionalProperties": false,
- "properties": {
- "image-size": {
- "type": "integer",
- "deprecated": true,
- "description": "deprecated (change the CSS root variable \"--mpris-album-art-icon-size\"): The size of the album art",
- "default": -1
- },
- "show-album-art": {
- "type": "string",
- "description": "Whether or not the album art should be hidden, always visible, or only visible when a valid album art is provided.",
- "default": "always",
- "enum": ["always", "when-available", "never"]
- },
- "autohide": {
- "type": "boolean",
- "description": "Whether to hide the widget when the player has no metadata.",
- "default": false
- },
- "blacklist": {
- "type": "array",
- "description": "Audio sources for the mpris widget to ignore.",
- "items": {
+ "notification-grouping": {
+ "type": "boolean",
+ "description": "If notifications should be grouped by app name",
+ "default": true
+ },
+ "image-visibility": {
+ "type": "string",
+ "description": "The notification image visibility when no icon is available.",
+ "default": "when-available",
+ "enum": ["always", "when-available", "never"]
+ },
+ "transition-time": {
+ "type": "integer",
+ "description": "The notification animation duration. 0 to disable",
+ "default": 200
+ },
+ "hide-on-clear": {
+ "type": "boolean",
+ "description": "Hides the control center after pressing \"Clear All\"",
+ "default": false
+ },
+ "hide-on-action": {
+ "type": "boolean",
+ "description": "Hides the control center when clicking on notification action",
+ "default": true
+ },
+ "text-empty": {
"type": "string",
- "description": "Audio source/app name. Regex allowed."
- }
+ "description": "Text that appears when there are no notifications to show",
+ "default": "No Notifications"
},
- "loop-carousel": {
- "type": "boolean",
- "description": "Whether to loop through the mpris carousel.",
- "default": "false"
- }
- }
- },
- "buttons-grid": {
- "type": "object",
- "description": "A widget to add a grid of buttons that execute shell commands",
- "additionalProperties": false,
- "properties": {
- "buttons-per-row": {
- "type": "number",
- "description": "How many buttons should be shown in a buttons-grid row"
- },
- "actions": {
- "type": "array",
- "description": "A list of actions containing a label and a command",
- "items": {
+ "script-fail-notify": {
+ "type": "boolean",
+ "description": "Sends a notification if a script fails to run",
+ "default": true
+ },
+ "scripts": {
"type": "object",
- "properties": {
- "label": {
- "type": "string",
- "description": "Text to be displayed in button",
- "default": "label"
- },
- "command": {
- "type": "string",
- "description": "Command to be executed on click",
- "default": ""
- },
- "type": {
- "type": "string",
- "description": "Type of the button; toggle buttons receive the .active css class and an env variable 'SWAYNC_TOGGLE_STATE' is set. See example in the default config.json",
- "default": "normal",
- "enum": ["normal", "toggle"]
- },
- "update-command": {
+ "description": "Which scripts to check and potentially run for every notification. If the notification doesn't include one of the properties, that property will be ignored. All properties (except for exec) use regex. If all properties match the given notification, the script will be run. Only the first matching script will be run.",
+ "minProperties": 1,
+ "additionalProperties": false,
+ "patternProperties": {
+ "^.{1,}$": {
+ "type": "object",
+ "description": "Your script object.",
+ "required": ["exec"],
+ "minProperties": 2,
+ "additionalProperties": false,
+ "properties": {
+ "exec": {
+ "type": "string",
+ "description": "The script to run. Can also run regular shell commands."
+ },
+ "app-name": {
+ "type": "string",
+ "description": "The app-name. Uses Regex."
+ },
+ "desktop-entry": {
+ "type": "string",
+ "description": "The desktop-entry. Uses Regex."
+ },
+ "summary": {
+ "type": "string",
+ "description": "The summary of the notification. Uses Regex."
+ },
+ "body": {
+ "type": "string",
+ "description": "The body of the notification. Uses Regex."
+ },
+ "urgency": {
+ "type": "string",
+ "description": "The urgency of the notification.",
+ "default": "Normal",
+ "enum": ["Low", "Normal", "Critical"]
+ },
+ "category": {
+ "type": "string",
+ "description": "Which category the notification belongs to. Uses Regex."
+ },
+ "sound-file": {
+ "type": "string",
+ "description": "Which sound file the notification requested. Uses Regex."
+ },
+ "sound-name": {
+ "type": "string",
+ "description": "Which sound name the notification requested. Uses Regex."
+ },
+ "run-on": {
+ "type": "string",
+ "description": "Whether to run the script on an action being activated, or when the notification is received.",
+ "enum": ["action", "receive"],
+ "default": "receive"
+ }
+ }
+ }
+ }
+ },
+ "notification-action-filter": {
+ "type": "object",
+ "description": "Hides matching action(s) of matching notifications. If the notification doesn't include one of the properties, that property will be ignored. If all properties match the given notification, the matching actions will be hidden.",
+ "minProperties": 1,
+ "additionalProperties": false,
+ "patternProperties": {
+ "^.{1,}$": {
+ "type": "object",
+ "description": "Your script object.",
+ "minProperties": 1,
+ "anyOf": [{ "required": ["id-matcher"] }, { "required": ["text-matcher"] }],
+ "additionalProperties": false,
+ "properties": {
+ "use-regex": {
+ "type": "boolean",
+ "default": false,
+ "description": "Indicates if all the below fields should use regex or not."
+ },
+ "app-name": {
+ "type": "string",
+ "description": "The app-name."
+ },
+ "desktop-entry": {
+ "type": "string",
+ "description": "The desktop-entry."
+ },
+ "id-matcher": {
+ "type": "string",
+ "description": "Matches the action identifier. Can be found by reading the output of swaync when run with the `G_MESSAGES_DEBUG=all` environment variable."
+ },
+ "text-matcher": {
+ "type": "string",
+ "description": "Matches the actions text visible in the notification."
+ }
+ }
+ }
+ }
+ },
+ "notification-visibility": {
+ "type": "object",
+ "description": "Set the visibility of each incoming notification. If the notification doesn't include one of the properties, that property will be ignored. All properties (except for state) use regex. If all properties match the given notification, the notification will be follow the provided state. Only the first matching object will be used.",
+ "minProperties": 1,
+ "additionalProperties": false,
+ "patternProperties": {
+ "^.{1,}$": {
+ "type": "object",
+ "description": "Your script object.",
+ "required": ["state"],
+ "minProperties": 2,
+ "additionalProperties": false,
+ "properties": {
+ "state": {
+ "type": "string",
+ "description": "The notification visibility state.",
+ "default": "enabled",
+ "enum": ["ignored", "muted", "enabled", "transient"]
+ },
+ "app-name": {
+ "type": "string",
+ "description": "The app-name. Uses Regex."
+ },
+ "desktop-entry": {
+ "type": "string",
+ "description": "The desktop-entry. Uses Regex."
+ },
+ "summary": {
+ "type": "string",
+ "description": "The summary of the notification. Uses Regex."
+ },
+ "body": {
+ "type": "string",
+ "description": "The body of the notification. Uses Regex."
+ },
+ "urgency": {
+ "type": "string",
+ "description": "The urgency of the notification.",
+ "default": "Normal",
+ "enum": ["Low", "Normal", "Critical"]
+ },
+ "override-urgency": {
+ "type": "string",
+ "description": "The new urgency of the notification (optional)",
+ "default": "unset",
+ "enum": ["unset", "low", "normal", "critical"]
+ },
+ "category": {
+ "type": "string",
+ "description": "Which category the notification belongs to. Uses Regex."
+ }
+ }
+ }
+ }
+ },
+ "widgets": {
+ "type": "array",
+ "uniqueItems": true,
+ "description": "Which order and which widgets to display. If the \"notifications\" widget isn't specified, it will be placed at the bottom.",
+ "default": ["inhibitors", "title", "dnd", "notifications"],
+ "items": {
"type": "string",
- "description": "Command to be executed on visibility change of cc to update the active state of the toggle button (should echo true or false)",
- "default": ""
- },
- "active": {
- "type": "boolean",
- "description": "Whether the toggle button is active as default or not",
- "default": false
- }
+ "$comment": "Sadly can't use regex and enums at the same time. Fix in the future?",
+ "pattern": "^[a-zA-Z0-9_-]{1,}(#[a-zA-Z0-9_-]{1,}){0,1}?$"
+ }
+ },
+ "widget-config": {
+ "type": "object",
+ "description": "Configure specific widget properties.",
+ "additionalProperties": false,
+ "$comment": "New widgets go here in \"patternProperties\" ↓",
+ "patternProperties": {
+ "notifications": {
+ "$comment": "References the widget structure from \"widgets\" below",
+ "$ref": "#/widgets/notifications"
+ },
+ "^title(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
+ "$comment": "References the widget structure from \"widgets\" below",
+ "$ref": "#/widgets/title"
+ },
+ "^dnd(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
+ "$ref": "#/widgets/dnd"
+ },
+ "^label(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
+ "$ref": "#/widgets/label"
+ },
+ "^mpris(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
+ "$ref": "#/widgets/mpris"
+ },
+ "^buttons-grid(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
+ "$ref": "#/widgets/buttons-grid"
+ },
+ "^menubar(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
+ "$ref": "#/widgets/menubar"
+ },
+ "^slider(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
+ "$ref": "#/widgets/slider"
+ },
+ "^volume(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
+ "$ref": "#/widgets/volume"
+ },
+ "^backlight(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
+ "$ref": "#/widgets/backlight"
+ },
+ "^inhibitors(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
+ "$comment": "References the widget structure from \"widgets\" below",
+ "$ref": "#/widgets/inhibitors"
+ }
}
- }
}
- }
},
- "menubar": {
- "type": "object",
- "description": "A bar that contains action-buttons and buttons to open a dropdown with action-buttons",
- "additionalProperties": false,
- "patternProperties": {
- "^menu(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
- "type": "object",
- "description": "A button that opens a dropdown with action-buttons",
- "additionalProperties": false,
- "properties": {
- "label": {
- "type": "string",
- "description": "Text to be displayed in button",
- "default": "Menu"
- },
- "position": {
- "type": "string",
- "description": "Horizontal position of the button in the bar",
- "default": "right",
- "enum": ["right", "left"]
- },
- "animation-type": {
- "type": "string",
- "default": "slide_down",
- "description": "Animation type for menu",
- "enum": ["slide_down", "slide_up", "none"]
- },
- "animation-duration": {
- "type": "integer",
- "default": 250,
- "description": "Duration of animation in milliseconds"
- },
- "actions": {
- "$ref": "#/widgets/buttons-grid/properties/actions"
+ "widgets": {
+ "$comment": "New widgets go here",
+ "notifications": {
+ "type": "object",
+ "description": "Control Center Title Widget",
+ "additionalProperties": false,
+ "properties": {
+ "vexpand": {
+ "type": "boolean",
+ "description": "Whether or not the notifications widget should vertically expand or not",
+ "default": true
+ }
}
- }
- },
- "^buttons(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
- "type": "object",
- "description": "A list of action-buttons to be displayed in the topbar",
- "additionalProperties": false,
- "properties": {
- "position": {
- "type": "string",
- "description": "Horizontal position of the button in the bar",
- "default": "right",
- "enum": ["right", "left"]
- },
- "actions": {
- "$ref": "#/widgets/buttons-grid/properties/actions"
+ },
+ "title": {
+ "type": "object",
+ "description": "Control Center Title Widget",
+ "additionalProperties": false,
+ "properties": {
+ "text": {
+ "type": "string",
+ "description": "The title of the widget",
+ "default": "Notifications"
+ },
+ "clear-all-button": {
+ "type": "boolean",
+ "description": "Whether to display a \"Clear All\" button",
+ "default": true
+ },
+ "button-text": {
+ "type": "string",
+ "description": "\"Clear All\" button text",
+ "default": "Clear All"
+ }
}
- }
- }
- }
- },
- "slider": {
- "type": "object",
- "description": "general slider control",
- "additionalProperties": false,
- "properties": {
- "label": {
- "type": "string",
- "description": "Text displayed in front of the slider",
- "default": "slider"
- },
- "cmd_setter": {
- "type": "string",
- "description": "command to set the value. Use $value to get the current value",
- "default": ""
- },
- "cmd_getter": {
- "type": "string",
- "description": "command to get the actual value. Use $value to get the current value",
- "default": ""
- },
- "min": {
- "type": "integer",
- "description": "minimum value of the slider range",
- "default": 0
- },
- "max": {
- "type": "integer",
- "description": "maximum value of the slider range",
- "default": 100
- },
- "min_limit": {
- "type": "integer",
- "description": "limit minimum value of the slider",
- "default": 0
- },
- "max_limit": {
- "type": "integer",
- "description": "limit maximum value of the slider",
- "default": 100
- },
- "value_scale": {
- "type": "integer",
- "default": 0,
- "description": "scale small value, slider round digits"
- }
- }
- },
- "volume": {
- "type": "object",
- "description": "Slider to control pulse volume",
- "additionalProperties": false,
- "properties": {
- "label": {
- "type": "string",
- "description": "Text displayed in front of the volume slider",
- "default": "Volume"
- },
- "show-per-app": {
- "type": "boolean",
- "default": false,
- "description": "Show per app volume control"
- },
- "show-per-app-icon": {
- "type": "boolean",
- "default": true,
- "description": "Show application icon in per app control"
- },
- "show-per-app-label": {
- "type": "boolean",
- "default": false,
- "description": "Show application name in per app control"
- },
- "expand-per-app": {
- "type": "boolean",
- "default": false,
- "description": "If the per app section should start expanded"
- },
- "empty-list-label": {
- "type": "string",
- "default": "No active sink input",
- "description": "Text displayed when there are not active sink inputs"
- },
- "expand-button-label": {
- "type": "string",
- "default": "⇧",
- "description": "Label displayed on button to show per app volume control"
- },
- "collapse-button-label": {
- "type": "string",
- "default": "⇩",
- "description": "Label displayed on button to hide per app volume control"
- },
- "icon-size": {
- "type": "integer",
- "deprecated": true,
- "default": -1,
- "description": "deprecated (change the CSS root variable \"--widget-volume-row-icon-size\"): Size of the application icon in per app volume control"
- },
- "animation-type": {
- "type": "string",
- "default": "slide_down",
- "description": "Animation type for menu",
- "enum": ["slide_down", "slide_up", "none"]
- },
- "animation-duration": {
- "type": "integer",
- "default": 250,
- "description": "Duration of animation in milliseconds"
- }
- }
- },
- "backlight": {
- "type": "object",
- "description": "Slider to control monitor brightness",
- "additionalProperties": false,
- "properties": {
+ },
+ "dnd": {
+ "type": "object",
+ "description": "Control Center Do Not Disturb Widget",
+ "additionalProperties": false,
+ "properties": {
+ "text": {
+ "type": "string",
+ "description": "The title of the widget",
+ "default": "Do Not Disturb"
+ }
+ }
+ },
"label": {
- "type": "string",
- "description": "Text displayed in front of the backlight slider",
- "default": "Brightness"
- },
- "device": {
- "type": "string",
- "description": "Name of monitor (find possible devices using `ls /sys/class/backlight` or `ls /sys/class/leds`)",
- "default": "intel_backlight"
- },
- "subsystem": {
- "type": "string",
- "description": "Kernel subsystem for brightness control",
- "default": "backlight",
- "enum": ["backlight", "leds"]
- },
- "min": {
- "type": "integer",
- "default": 0,
- "description": "Lowest possible value for brightness"
- }
- }
- },
- "inhibitors": {
- "type": "object",
- "description": "Control Center Inhibitors Widget",
- "additionalProperties": false,
- "properties": {
- "text": {
- "type": "string",
- "description": "The title of the widget",
- "default": "Inhibitors"
- },
- "clear-all-button": {
- "type": "boolean",
- "description": "Whether to display a \"Clear All\" button",
- "default": true
- },
- "button-text": {
- "type": "string",
- "description": "\"Clear All\" button text",
- "default": "Clear All"
+ "type": "object",
+ "description": "A generic widget that allows the user to add custom text",
+ "additionalProperties": false,
+ "properties": {
+ "text": {
+ "type": "string",
+ "description": "The text content of the widget",
+ "default": "Label Text"
+ },
+ "max-lines": {
+ "type": "integer",
+ "description": "The maximum lines",
+ "default": 5
+ }
+ }
+ },
+ "mpris": {
+ "type": "object",
+ "description": "A widget that displays multiple music players",
+ "additionalProperties": false,
+ "properties": {
+ "image-size": {
+ "type": "integer",
+ "deprecated": true,
+ "description": "deprecated (change the CSS root variable \"--mpris-album-art-icon-size\"): The size of the album art",
+ "default": -1
+ },
+ "show-album-art": {
+ "type": "string",
+ "description": "Whether or not the album art should be hidden, always visible, or only visible when a valid album art is provided.",
+ "default": "always",
+ "enum": ["always", "when-available", "never"]
+ },
+ "autohide": {
+ "type": "boolean",
+ "description": "Whether to hide the widget when the player has no metadata.",
+ "default": false
+ },
+ "blacklist": {
+ "type": "array",
+ "description": "Audio sources for the mpris widget to ignore.",
+ "items": {
+ "type": "string",
+ "description": "Audio source/app name. Regex allowed."
+ }
+ },
+ "loop-carousel": {
+ "type": "boolean",
+ "description": "Whether to loop through the mpris carousel.",
+ "default": "false"
+ },
+ "show-title": {
+ "type": "boolean",
+ "description": "Whether to show the main title / player name in the mpris widget.",
+ "default": true
+ },
+ "show-subtitle": {
+ "type": "boolean",
+ "description": "Whether to show the subtitle (artist - album).",
+ "default": true
+ },
+ "show-background": {
+ "type": "boolean",
+ "description": "Whether to show the blurred background album art.",
+ "default": true
+ },
+ "show-shuffle": {
+ "type": "boolean",
+ "description": "Whether to show the shuffle button.",
+ "default": true
+ },
+ "show-repeat": {
+ "type": "boolean",
+ "description": "Whether to show the repeat button.",
+ "default": true
+ },
+ "show-favorite": {
+ "type": "boolean",
+ "description": "Whether to show a favorite/heart button (if present).",
+ "default": true
+ },
+ "button-size": {
+ "type": "integer",
+ "description": "Optional pixel size override for control buttons. -1 to use default.",
+ "default": -1
+ },
+ "compact-mode": {
+ "type": "boolean",
+ "description": "If true, use a compact layout optimized for small heights.",
+ "default": false
+ }
+ }
+ },
+ "buttons-grid": {
+ "type": "object",
+ "description": "A widget to add a grid of buttons that execute shell commands",
+ "additionalProperties": false,
+ "properties": {
+ "buttons-per-row": {
+ "type": "number",
+ "description": "How many buttons should be shown in a buttons-grid row"
+ },
+ "actions": {
+ "type": "array",
+ "description": "A list of actions containing a label and a command",
+ "items": {
+ "type": "object",
+ "properties": {
+ "label": {
+ "type": "string",
+ "description": "Text to be displayed in button",
+ "default": "label"
+ },
+ "command": {
+ "type": "string",
+ "description": "Command to be executed on click",
+ "default": ""
+ },
+ "type": {
+ "type": "string",
+ "description": "Type of the button; toggle buttons receive the .active css class and an env variable 'SWAYNC_TOGGLE_STATE' is set. See example in the default config.json",
+ "default": "normal",
+ "enum": ["normal", "toggle"]
+ },
+ "update-command": {
+ "type": "string",
+ "description": "Command to be executed on visibility change of cc to update the active state of the toggle button (should echo true or false)",
+ "default": ""
+ },
+ "active": {
+ "type": "boolean",
+ "description": "Whether the toggle button is active as default or not",
+ "default": false
+ }
+ }
+ }
+ }
+ }
+ },
+ "menubar": {
+ "type": "object",
+ "description": "A bar that contains action-buttons and buttons to open a dropdown with action-buttons",
+ "additionalProperties": false,
+ "patternProperties": {
+ "^menu(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
+ "type": "object",
+ "description": "A button that opens a dropdown with action-buttons",
+ "additionalProperties": false,
+ "properties": {
+ "label": {
+ "type": "string",
+ "description": "Text to be displayed in button",
+ "default": "Menu"
+ },
+ "position": {
+ "type": "string",
+ "description": "Horizontal position of the button in the bar",
+ "default": "right",
+ "enum": ["right", "left"]
+ },
+ "animation-type": {
+ "type": "string",
+ "default": "slide_down",
+ "description": "Animation type for menu",
+ "enum": ["slide_down", "slide_up", "none"]
+ },
+ "animation-duration": {
+ "type": "integer",
+ "default": 250,
+ "description": "Duration of animation in milliseconds"
+ },
+ "actions": {
+ "$ref": "#/widgets/buttons-grid/properties/actions"
+ }
+ }
+ },
+ "^buttons(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
+ "type": "object",
+ "description": "A list of action-buttons to be displayed in the topbar",
+ "additionalProperties": false,
+ "properties": {
+ "position": {
+ "type": "string",
+ "description": "Horizontal position of the button in the bar",
+ "default": "right",
+ "enum": ["right", "left"]
+ },
+ "actions": {
+ "$ref": "#/widgets/buttons-grid/properties/actions"
+ }
+ }
+ }
+ }
+ },
+ "slider": {
+ "type": "object",
+ "description": "general slider control",
+ "additionalProperties": false,
+ "properties": {
+ "label": {
+ "type": "string",
+ "description": "Text displayed in front of the slider",
+ "default": "slider"
+ },
+ "cmd_setter": {
+ "type": "string",
+ "description": "command to set the value. Use $value to get the current value",
+ "default": ""
+ },
+ "cmd_getter": {
+ "type": "string",
+ "description": "command to get the actual value. Use $value to get the current value",
+ "default": ""
+ },
+ "min": {
+ "type": "integer",
+ "description": "minimum value of the slider range",
+ "default": 0
+ },
+ "max": {
+ "type": "integer",
+ "description": "maximum value of the slider range",
+ "default": 100
+ },
+ "min_limit": {
+ "type": "integer",
+ "description": "limit minimum value of the slider",
+ "default": 0
+ },
+ "max_limit": {
+ "type": "integer",
+ "description": "limit maximum value of the slider",
+ "default": 100
+ },
+ "value_scale": {
+ "type": "integer",
+ "default": 0,
+ "description": "scale small value, slider round digits"
+ }
+ }
+ },
+ "volume": {
+ "type": "object",
+ "description": "Slider to control pulse volume",
+ "additionalProperties": false,
+ "properties": {
+ "label": {
+ "type": "string",
+ "description": "Text displayed in front of the volume slider",
+ "default": "Volume"
+ },
+ "show-per-app": {
+ "type": "boolean",
+ "default": false,
+ "description": "Show per app volume control"
+ },
+ "show-per-app-icon": {
+ "type": "boolean",
+ "default": true,
+ "description": "Show application icon in per app control"
+ },
+ "show-per-app-label": {
+ "type": "boolean",
+ "default": false,
+ "description": "Show application name in per app control"
+ },
+ "expand-per-app": {
+ "type": "boolean",
+ "default": false,
+ "description": "If the per app section should start expanded"
+ },
+ "empty-list-label": {
+ "type": "string",
+ "default": "No active sink input",
+ "description": "Text displayed when there are not active sink inputs"
+ },
+ "expand-button-label": {
+ "type": "string",
+ "default": "⇧",
+ "description": "Label displayed on button to show per app volume control"
+ },
+ "collapse-button-label": {
+ "type": "string",
+ "default": "⇩",
+ "description": "Label displayed on button to hide per app volume control"
+ },
+ "icon-size": {
+ "type": "integer",
+ "deprecated": true,
+ "default": -1,
+ "description": "deprecated (change the CSS root variable \"--widget-volume-row-icon-size\"): Size of the application icon in per app volume control"
+ },
+ "animation-type": {
+ "type": "string",
+ "default": "slide_down",
+ "description": "Animation type for menu",
+ "enum": ["slide_down", "slide_up", "none"]
+ },
+ "animation-duration": {
+ "type": "integer",
+ "default": 250,
+ "description": "Duration of animation in milliseconds"
+ }
+ }
+ },
+ "backlight": {
+ "type": "object",
+ "description": "Slider to control monitor brightness",
+ "additionalProperties": false,
+ "properties": {
+ "label": {
+ "type": "string",
+ "description": "Text displayed in front of the backlight slider",
+ "default": "Brightness"
+ },
+ "device": {
+ "type": "string",
+ "description": "Name of monitor (find possible devices using `ls /sys/class/backlight` or `ls /sys/class/leds`)",
+ "default": "intel_backlight"
+ },
+ "subsystem": {
+ "type": "string",
+ "description": "Kernel subsystem for brightness control",
+ "default": "backlight",
+ "enum": ["backlight", "leds"]
+ },
+ "min": {
+ "type": "integer",
+ "default": 0,
+ "description": "Lowest possible value for brightness"
+ }
+ }
+ },
+ "inhibitors": {
+ "type": "object",
+ "description": "Control Center Inhibitors Widget",
+ "additionalProperties": false,
+ "properties": {
+ "text": {
+ "type": "string",
+ "description": "The title of the widget",
+ "default": "Inhibitors"
+ },
+ "clear-all-button": {
+ "type": "boolean",
+ "description": "Whether to display a \"Clear All\" button",
+ "default": true
+ },
+ "button-text": {
+ "type": "string",
+ "description": "\"Clear All\" button text",
+ "default": "Clear All"
+ }
+ }
}
- }
}
- }
}
diff --git a/src/controlCenter/widgets/mpris/mpris.vala b/src/controlCenter/widgets/mpris/mpris.vala
index 4448b4fb..bd19726c 100644
--- a/src/controlCenter/widgets/mpris/mpris.vala
+++ b/src/controlCenter/widgets/mpris/mpris.vala
@@ -22,6 +22,15 @@ namespace SwayNotificationCenter.Widgets.Mpris {
bool autohide;
string[] blacklist;
bool loop_carousel;
+ // New customization options
+ bool show_title;
+ bool show_subtitle;
+ bool show_background;
+ bool show_shuffle;
+ bool show_repeat;
+ bool show_favorite;
+ int button_size;
+ bool compact_mode;
}
public class Mpris : BaseWidget {
@@ -49,6 +58,15 @@ namespace SwayNotificationCenter.Widgets.Mpris {
show_album_art = AlbumArtState.ALWAYS,
autohide = false,
loop_carousel = false,
+ // defaults for new options
+ show_title = true,
+ show_subtitle = true,
+ show_background = true,
+ show_shuffle = true,
+ show_repeat = true,
+ show_favorite = true,
+ button_size = -1,
+ compact_mode = false,
};
public Mpris (string suffix, SwayncDaemon swaync_daemon, NotiDaemon noti_daemon) {
@@ -143,6 +161,65 @@ namespace SwayNotificationCenter.Widgets.Mpris {
if (loop_carousel_found) {
mpris_config.loop_carousel = loop_carousel;
}
+
+ // New options
+ bool show_title_found;
+ bool ?show_title = get_prop (config, "show-title", out show_title_found);
+ if (show_title_found && show_title != null) {
+ mpris_config.show_title = show_title;
+ }
+
+ bool show_subtitle_found;
+ bool ?show_subtitle = get_prop (
+ config, "show-subtitle", out show_subtitle_found
+ );
+ if (show_subtitle_found && show_subtitle != null) {
+ mpris_config.show_subtitle = show_subtitle;
+ }
+
+ bool show_background_found;
+ bool ?show_background = get_prop (
+ config, "show-background", out show_background_found
+ );
+ if (show_background_found && show_background != null) {
+ mpris_config.show_background = show_background;
+ }
+
+ bool show_shuffle_found;
+ bool ?show_shuffle = get_prop (
+ config, "show-shuffle", out show_shuffle_found
+ );
+ if (show_shuffle_found && show_shuffle != null) {
+ mpris_config.show_shuffle = show_shuffle;
+ }
+
+ bool show_repeat_found;
+ bool ?show_repeat = get_prop (config, "show-repeat", out show_repeat_found);
+ if (show_repeat_found && show_repeat != null) {
+ mpris_config.show_repeat = show_repeat;
+ }
+
+ bool show_favorite_found;
+ bool ?show_favorite = get_prop (
+ config, "show-favorite", out show_favorite_found
+ );
+ if (show_favorite_found && show_favorite != null) {
+ mpris_config.show_favorite = show_favorite;
+ }
+
+ bool button_size_found;
+ int ?button_size = get_prop (config, "button-size", out button_size_found);
+ if (button_size_found && button_size != null) {
+ mpris_config.button_size = button_size;
+ }
+
+ bool compact_mode_found;
+ bool ?compact_mode = get_prop (
+ config, "compact-mode", out compact_mode_found
+ );
+ if (compact_mode_found && compact_mode != null) {
+ mpris_config.compact_mode = compact_mode;
+ }
}
hide ();
diff --git a/src/controlCenter/widgets/mpris/mpris_player.vala b/src/controlCenter/widgets/mpris/mpris_player.vala
index 0aa47dcc..1ef02ad8 100644
--- a/src/controlCenter/widgets/mpris/mpris_player.vala
+++ b/src/controlCenter/widgets/mpris/mpris_player.vala
@@ -208,6 +208,8 @@ namespace SwayNotificationCenter.Widgets.Mpris {
}
title.set_text (name);
}
+ // Honor show_title config
+ title.set_visible (mpris_config.show_title);
}
private void update_sub_title (HashTable metadata) {
@@ -246,8 +248,9 @@ namespace SwayNotificationCenter.Widgets.Mpris {
}
}
sub_title.set_text (result);
- // Hide if no album or artist
- sub_title.set_visible (result.length > 0);
+ // Hide if no album or artist or config disables it
+ bool should_show = result.length > 0 && mpris_config.show_subtitle;
+ sub_title.set_visible (should_show);
}
private async void update_album_art (HashTable metadata) {
@@ -329,6 +332,7 @@ namespace SwayNotificationCenter.Widgets.Mpris {
// Set background album art
background_picture.set_paintable (album_art_texture);
+ background_picture.set_visible (mpris_config.show_background);
this.queue_draw ();
return;
@@ -349,6 +353,7 @@ namespace SwayNotificationCenter.Widgets.Mpris {
if (icon_info != null) {
album_art.set_from_gicon (icon);
background_picture.set_paintable (icon_info);
+ background_picture.set_visible (mpris_config.show_background);
debug ("MPRIS album art: using .desktop icon");
return;
@@ -362,6 +367,7 @@ namespace SwayNotificationCenter.Widgets.Mpris {
icon_name, null, 128, get_scale_factor (),
Gtk.TextDirection.NONE, 0);
background_picture.set_paintable (icon_info);
+ background_picture.set_visible (mpris_config.show_background);
debug ("MPRIS album art: using default icon");
}
@@ -370,6 +376,12 @@ namespace SwayNotificationCenter.Widgets.Mpris {
if (!(button_shuffle is Gtk.Widget)) {
return;
}
+ // Respect user config: hide shuffle entirely if disabled
+ if (!mpris_config.show_shuffle) {
+ button_shuffle.set_visible (false);
+ button_shuffle.hide ();
+ return;
+ }
if (source.media_player.can_control) {
// Shuffle check
@@ -434,6 +446,12 @@ namespace SwayNotificationCenter.Widgets.Mpris {
if (!(button_repeat is Gtk.Widget)) {
return;
}
+ // Respect user config: hide repeat entirely if disabled
+ if (!mpris_config.show_repeat) {
+ button_repeat.set_visible (false);
+ button_repeat.hide ();
+ return;
+ }
if (source.media_player.can_control) {
// Repeat check
diff --git a/test-mpris-configs.sh b/test-mpris-configs.sh
new file mode 100755
index 00000000..563512b8
--- /dev/null
+++ b/test-mpris-configs.sh
@@ -0,0 +1,167 @@
+#!/bin/bash
+# Script para testar diferentes configurações do MPRIS
+
+CONFIG_FILE="$HOME/.config/hypr/swaync/config.json"
+BACKUP_FILE="$HOME/.config/hypr/swaync/config.json.backup"
+
+# Função para aplicar uma configuração
+apply_config() {
+ local config_name=$1
+ echo "📝 Aplicando configuração: $config_name"
+
+ # Backup da config atual
+ cp "$CONFIG_FILE" "$BACKUP_FILE"
+
+ # Aplicar nova configuração MPRIS
+ case $config_name in
+ "ultra-compact")
+ cat > /tmp/mpris_config.json << 'EOF'
+{
+ "show-album-art": "never",
+ "show-title": false,
+ "show-subtitle": false,
+ "show-background": false,
+ "show-shuffle": false,
+ "show-repeat": false
+}
+EOF
+ ;;
+
+ "minimal")
+ cat > /tmp/mpris_config.json << 'EOF'
+{
+ "show-album-art": "when-available",
+ "show-title": true,
+ "show-subtitle": false,
+ "show-background": true,
+ "show-shuffle": false,
+ "show-repeat": false
+}
+EOF
+ ;;
+
+ "complete")
+ cat > /tmp/mpris_config.json << 'EOF'
+{
+ "show-album-art": "always",
+ "show-title": true,
+ "show-subtitle": true,
+ "show-background": true,
+ "show-shuffle": true,
+ "show-repeat": true
+}
+EOF
+ ;;
+
+ "no-art")
+ cat > /tmp/mpris_config.json << 'EOF'
+{
+ "show-album-art": "never",
+ "show-title": true,
+ "show-subtitle": true,
+ "show-background": false,
+ "show-shuffle": true,
+ "show-repeat": true
+}
+EOF
+ ;;
+
+ "restore")
+ echo "♻️ Restaurando backup..."
+ cp "$BACKUP_FILE" "$CONFIG_FILE"
+ swaync-client --reload-config
+ echo "✅ Configuração restaurada!"
+ return
+ ;;
+
+ *)
+ echo "❌ Configuração desconhecida: $config_name"
+ echo "Opções: ultra-compact, minimal, complete, no-art, restore"
+ return 1
+ ;;
+ esac
+
+ # Validar JSON gerado
+ if ! jq empty /tmp/mpris_config.json 2>/dev/null; then
+ echo "❌ Erro: JSON inválido gerado"
+ return 1
+ fi
+
+ # Atualizar o config.json com a nova configuração mpris
+ if ! jq --slurpfile mpris /tmp/mpris_config.json \
+ '.["widget-config"]["mpris"] = $mpris[0]' \
+ "$CONFIG_FILE" > /tmp/config_new.json; then
+ echo "❌ Erro ao processar configuração"
+ return 1
+ fi
+
+ # Validar JSON final
+ if ! jq empty /tmp/config_new.json 2>/dev/null; then
+ echo "❌ Erro: Configuração final inválida"
+ echo "🔄 Restaurando backup..."
+ cp "$BACKUP_FILE" "$CONFIG_FILE"
+ return 1
+ fi
+
+ mv /tmp/config_new.json "$CONFIG_FILE"
+
+ # Recarregar SwayNC
+ swaync-client --reload-config
+
+ echo "✅ Configuração '$config_name' aplicada!"
+ echo "📱 Teste reproduzindo música para ver as mudanças"
+}
+
+# Menu interativo
+show_menu() {
+ echo ""
+ echo "🎵 MPRIS Configuration Tester"
+ echo "=============================="
+ echo ""
+ echo "Escolha uma configuração:"
+ echo ""
+ echo " 1) Ultra-Compact - Apenas 3 botões (⏮️ ⏯️ ⏭️)"
+ echo " 2) Minimal - Capa + título + 3 botões"
+ echo " 3) Complete - Todos os elementos"
+ echo " 4) No-Art - Sem imagens, todos os botões"
+ echo " 5) Restore - Restaurar backup"
+ echo " q) Sair"
+ echo ""
+ read -p "Opção: " choice
+
+ case $choice in
+ 1) apply_config "ultra-compact" ;;
+ 2) apply_config "minimal" ;;
+ 3) apply_config "complete" ;;
+ 4) apply_config "no-art" ;;
+ 5) apply_config "restore" ;;
+ q|Q) exit 0 ;;
+ *) echo "❌ Opção inválida"; show_menu ;;
+ esac
+}
+
+# Verificar dependências
+if ! command -v jq &> /dev/null; then
+ echo "❌ Erro: jq não está instalado"
+ echo "Instale com: sudo pacman -S jq"
+ exit 1
+fi
+
+if ! command -v swaync-client &> /dev/null; then
+ echo "❌ Erro: swaync-client não encontrado"
+ exit 1
+fi
+
+# Verificar se config existe
+if [ ! -f "$CONFIG_FILE" ]; then
+ echo "❌ Erro: $CONFIG_FILE não existe"
+ exit 1
+fi
+
+# Se argumento foi passado, aplicar diretamente
+if [ $# -eq 1 ]; then
+ apply_config "$1"
+else
+ # Modo interativo
+ show_menu
+fi