mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
Merge branch 'main' into pr/refactor-battery-pt5
This commit is contained in:
@@ -211,6 +211,10 @@
|
||||
"memory-usage-label": "Speicherauslastung",
|
||||
"network-traffic-description": "Upload- und Download-Geschwindigkeiten anzeigen.",
|
||||
"network-traffic-label": "Netzwerkverkehr",
|
||||
"storage-as-percentage-description": "Festplattenspeicher als Prozentsatz statt absoluter Werte anzeigen.",
|
||||
"storage-as-percentage-label": "Festplatte als Prozentsatz",
|
||||
"storage-available-description": "Zeigt an, wie viel Speicherplatz verfügbar ist, anstatt wie viel belegt ist.",
|
||||
"storage-available-label": "Verfügbarer Speicherplatz",
|
||||
"storage-usage-description": "Festplattenspeicher-Nutzungsinformationen anzeigen.",
|
||||
"storage-usage-label": "Datenträgerauslastung",
|
||||
"swap-usage-description": "Swap-Speichernutzung anzeigen.",
|
||||
@@ -272,6 +276,8 @@
|
||||
"label-mode-label": "Beschriftungsmodus",
|
||||
"occupied-color-description": "Lege die Hintergrundfarbe für belegte Arbeitsflächen fest.",
|
||||
"occupied-color-label": "Farbe für belegte Arbeitsfläche",
|
||||
"pill-size-description": "Passen Sie die Größe der Arbeitsbereichs-Pillen an (50%-100%).",
|
||||
"pill-size-label": "Pillengröße",
|
||||
"reverse-scrolling-description": "Die Richtung des Arbeitsflächenwechsels beim Scrollen umkehren.",
|
||||
"reverse-scrolling-label": "Scrollen umkehren",
|
||||
"show-applications-description": "Anwendungssymbole in jeder Arbeitsfläche anzeigen.",
|
||||
@@ -387,6 +393,7 @@
|
||||
"disconnecting": "Verbindung wird getrennt...",
|
||||
"download": "Herunterladen",
|
||||
"duration": "Dauer",
|
||||
"dysfunctional": "Dysfunktional",
|
||||
"edit": "Bearbeiten",
|
||||
"enabled": "Aktiviert",
|
||||
"events": "Ereignisse",
|
||||
@@ -807,6 +814,7 @@
|
||||
"download-title": "Farbschemata herunterladen",
|
||||
"method-description": {
|
||||
"content": "Material Design Schema mit hochauflösender Farbextraktion, das die tatsächlichen Farben des Quellinhalts genau wiedergibt.",
|
||||
"dysfunctional": "Wie Faithful, wählt aber die zweitdominanteste Farbfamilie als primär.",
|
||||
"faithful": "Versucht, nah an der Ausgangsfarbe zu bleiben, während gleichzeitig eine harmonische Palette erzeugt wird.",
|
||||
"fruit-salad": "Material Design-Schema, das eine spielerische, lebendige Palette mit vielfältigen Farbtönen erzeugt.",
|
||||
"monochrome": "Material Design-Schema mit einer einfarbigen Grauskala und minimalem Farbanteil.",
|
||||
@@ -1302,6 +1310,7 @@
|
||||
"sources-placeholder": "Mein cooles Repository",
|
||||
"sources-remove-tooltip": "Plugin-Quelle entfernen",
|
||||
"title": "Plugins",
|
||||
"translations-reloaded": "Übersetzungen neu geladen: {name}",
|
||||
"uninstall-dialog-description": "Sind Sie sicher, dass Sie {plugin} deinstallieren möchten? Dadurch werden alle Plugin-Daten entfernt.",
|
||||
"uninstall-dialog-title": "Plugin deinstallieren",
|
||||
"uninstall-error": "Deinstallation fehlgeschlagen: {error}",
|
||||
@@ -1349,6 +1358,7 @@
|
||||
"system-monitor": {
|
||||
"critical-color-label": "Kritische Farbe",
|
||||
"custom-highlight-colors-title-label": "Benutzerdefinierte Hervorhebungsfarben",
|
||||
"disk-available-label": "Verfügbarer Speicherplatz",
|
||||
"disk-section-label": "Festplattennutzung",
|
||||
"enable-dgpu-monitoring-description": "Warnung: Dies aktiviert Ihre dedizierte GPU (NVIDIA/AMD), was die Akkulaufzeit von Laptops mit Hybridgrafik erheblich beeinträchtigen kann.",
|
||||
"enable-dgpu-monitoring-label": "Dedizierte GPU-Überwachung aktivieren",
|
||||
|
||||
@@ -211,6 +211,10 @@
|
||||
"memory-usage-label": "Memory usage",
|
||||
"network-traffic-description": "Display network upload and download speeds.",
|
||||
"network-traffic-label": "Network traffic",
|
||||
"storage-as-percentage-description": "Show disk space as percentage instead of absolute values.",
|
||||
"storage-as-percentage-label": "Disk as percentage",
|
||||
"storage-available-description": "Shows how much disk space is available instead of how much is used.",
|
||||
"storage-available-label": "Disk space available",
|
||||
"storage-usage-description": "Show disk space usage information.",
|
||||
"storage-usage-label": "Storage usage",
|
||||
"swap-usage-description": "Show swap memory usage.",
|
||||
@@ -272,6 +276,8 @@
|
||||
"label-mode-label": "Label mode",
|
||||
"occupied-color-description": "Set the background color for occupied workspaces.",
|
||||
"occupied-color-label": "Occupied workspace color",
|
||||
"pill-size-description": "Adjust the size of workspace pills.",
|
||||
"pill-size-label": "Pill size",
|
||||
"reverse-scrolling-description": "Reverse the direction of workspace switching when scrolling.",
|
||||
"reverse-scrolling-label": "Reverse scrolling",
|
||||
"show-applications-description": "Display application icons inside each workspace.",
|
||||
@@ -386,6 +392,7 @@
|
||||
"disconnecting": "Disconnecting...",
|
||||
"download": "Download",
|
||||
"duration": "Duration",
|
||||
"dysfunctional": "Dysfunctional",
|
||||
"edit": "Edit",
|
||||
"enabled": "Enabled",
|
||||
"events": "Events",
|
||||
@@ -806,6 +813,7 @@
|
||||
"download-title": "Download Color Schemes",
|
||||
"method-description": {
|
||||
"content": "Material Design scheme with high-fidelity color extraction that closely matches the source content's actual colors.",
|
||||
"dysfunctional": "Like Faithful but picks the second most dominant color family as primary.",
|
||||
"faithful": "Attempts to stay close to the source color while still generating a harmonious palette.",
|
||||
"fruit-salad": "Material Design scheme that produces a playful, vibrant palette with diverse and varied hues.",
|
||||
"monochrome": "Material Design scheme using a single-hue grayscale with minimal chromatic content.",
|
||||
@@ -1274,7 +1282,6 @@
|
||||
"hot-reload-description": "Automatically reload plugins when their files change. Useful for plugin development.",
|
||||
"hot-reload-label": "Hot reload (dev mode)",
|
||||
"hot-reloaded": "Reloaded plugin: {name}",
|
||||
"translations-reloaded": "Reloaded translations: {name}",
|
||||
"install-error": "Failed to install: {error}",
|
||||
"install-incompatible": "{plugin} requires Noctalia v{version} or higher",
|
||||
"install-success": "Successfully installed {plugin}",
|
||||
@@ -1304,6 +1311,7 @@
|
||||
"sources-placeholder": "My cool repository",
|
||||
"sources-remove-tooltip": "Remove plugin source",
|
||||
"title": "Plugins",
|
||||
"translations-reloaded": "Reloaded translations: {name}",
|
||||
"uninstall-dialog-description": "Are you sure you want to uninstall {plugin}? This will remove all plugin data.",
|
||||
"uninstall-dialog-title": "Uninstall plugin",
|
||||
"uninstall-error": "Failed to uninstall: {error}",
|
||||
@@ -1351,6 +1359,7 @@
|
||||
"system-monitor": {
|
||||
"critical-color-label": "Critical color",
|
||||
"custom-highlight-colors-title-label": "Custom highlight colors",
|
||||
"disk-available-label": "Disk available",
|
||||
"disk-section-label": "Disk usage",
|
||||
"enable-dgpu-monitoring-description": "Warning: This will wake up your discrete GPU (NVIDIA/AMD), which may significantly impact battery life on laptops with hybrid graphics.",
|
||||
"enable-dgpu-monitoring-label": "Enable discrete GPU monitoring",
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"device-description": "Seleccione qué dispositivo de batería mostrar.",
|
||||
"device-label": "Dispositivo de batería",
|
||||
"hide-if-idle-description": "Ocultar el widget cuando la batería no se esté cargando ni descargando.",
|
||||
"hide-if-idle-label": "Ocultar cuando esté inactivo",
|
||||
"hide-if-idle-label": "Ocultar cuando inactivo",
|
||||
"hide-if-not-detected-description": "Ocultar el widget cuando no se detecte batería en el sistema.",
|
||||
"hide-if-not-detected-label": "Ocultar si no se detecta",
|
||||
"low-battery-threshold-description": "Muestra una advertencia cuando la batería cae por debajo de este porcentaje.",
|
||||
@@ -145,7 +145,7 @@
|
||||
},
|
||||
"lock-keys": {
|
||||
"hide-when-off-description": "Ocultar el indicador cuando la tecla no está activa.",
|
||||
"hide-when-off-label": "Ocultar cuando está desactivado",
|
||||
"hide-when-off-label": "Ocultar cuando desactivado",
|
||||
"show-caps-lock-description": "Mostrar el estado de Bloq Mayús.",
|
||||
"show-caps-lock-label": "Bloq Mayús",
|
||||
"show-num-lock-description": "Mostrar el estado de Bloq Num.",
|
||||
@@ -211,6 +211,10 @@
|
||||
"memory-usage-label": "Uso de memoria",
|
||||
"network-traffic-description": "Mostrar las velocidades de carga y descarga de la red.",
|
||||
"network-traffic-label": "Tráfico de red",
|
||||
"storage-as-percentage-description": "Mostrar el espacio en disco como porcentaje en lugar de valores absolutos.",
|
||||
"storage-as-percentage-label": "Disco como porcentaje",
|
||||
"storage-available-description": "Muestra cuánto espacio en disco está disponible en lugar de cuánto se usa.",
|
||||
"storage-available-label": "Espacio en disco disponible",
|
||||
"storage-usage-description": "Mostrar la información del uso del espacio en disco.",
|
||||
"storage-usage-label": "Uso de almacenamiento",
|
||||
"swap-usage-description": "Mostrar el uso de la memoria swap.",
|
||||
@@ -272,6 +276,8 @@
|
||||
"label-mode-label": "Modo de etiqueta",
|
||||
"occupied-color-description": "Establecer el color de fondo para los Workspaces ocupados.",
|
||||
"occupied-color-label": "Color del espacio de trabajo ocupado",
|
||||
"pill-size-description": "Ajusta el tamaño de las píldoras del espacio de trabajo (50%-100%).",
|
||||
"pill-size-label": "Tamaño de la píldora",
|
||||
"reverse-scrolling-description": "Invertir la dirección del cambio de espacios de trabajo al desplazarse.",
|
||||
"reverse-scrolling-label": "Desplazamiento inverso",
|
||||
"show-applications-description": "Mostrar los iconos de las aplicaciones dentro de cada espacio de trabajo.",
|
||||
@@ -387,6 +393,7 @@
|
||||
"disconnecting": "Desconectando...",
|
||||
"download": "Descargar",
|
||||
"duration": "Duración",
|
||||
"dysfunctional": "Disfuncional",
|
||||
"edit": "Editar",
|
||||
"enabled": "Activado",
|
||||
"events": "Eventos",
|
||||
@@ -510,8 +517,8 @@
|
||||
},
|
||||
"hide-modes": {
|
||||
"auto-hide": "Ocultación Automática",
|
||||
"hidden": "Ocultar cuando esté vacío",
|
||||
"idle": "Ocultar cuando esté inactivo",
|
||||
"hidden": "Ocultar cuando vacío",
|
||||
"idle": "Ocultar cuando inactivo",
|
||||
"transparent": "Transparente cuando esté vacío",
|
||||
"visible": "Mostrar siempre"
|
||||
},
|
||||
@@ -612,7 +619,7 @@
|
||||
"frame-rates-fps": "{fps} FPS",
|
||||
"scrolling-modes": {
|
||||
"always": "Desplazar siempre",
|
||||
"hover": "Desplazar al posar el puntero",
|
||||
"hover": "Desplazar encima",
|
||||
"never": "No desplazar nunca"
|
||||
},
|
||||
"session-menu-grid-layout": {
|
||||
@@ -807,6 +814,7 @@
|
||||
"download-title": "Descargar Esquemas de Color",
|
||||
"method-description": {
|
||||
"content": "Esquema de Material Design con extracción de color de alta fidelidad que coincide estrechamente con los colores reales del contenido de origen.",
|
||||
"dysfunctional": "Como Faithful, pero elige la segunda familia de colores más dominante como primaria.",
|
||||
"faithful": "Intenta mantenerse cerca del color original mientras genera una paleta armoniosa.",
|
||||
"fruit-salad": "Esquema de Material Design que produce una paleta alegre y vibrante con matices diversos y variados.",
|
||||
"monochrome": "Esquema de Material Design que utiliza una escala de grises de un solo tono con un contenido cromático mínimo.",
|
||||
@@ -1302,6 +1310,7 @@
|
||||
"sources-placeholder": "Mi repositorio genial",
|
||||
"sources-remove-tooltip": "Eliminar fuente del plugin",
|
||||
"title": "Plugins",
|
||||
"translations-reloaded": "Traducciones recargadas: {name}",
|
||||
"uninstall-dialog-description": "¿Está seguro de que quiere desinstalar {plugin}? Esto eliminará todos los datos del plugin.",
|
||||
"uninstall-dialog-title": "Desinstalar plugin",
|
||||
"uninstall-error": "Error al desinstalar: {error}",
|
||||
@@ -1349,6 +1358,7 @@
|
||||
"system-monitor": {
|
||||
"critical-color-label": "Color crítico",
|
||||
"custom-highlight-colors-title-label": "Colores de resaltado personalizados",
|
||||
"disk-available-label": "Espacio en disco disponible",
|
||||
"disk-section-label": "Uso de disco",
|
||||
"enable-dgpu-monitoring-description": "Advertencia: Esto activará tu GPU dedicada (NVIDIA/AMD), lo que podría afectar significativamente la duración de la batería en portátiles con gráficos híbridos.",
|
||||
"enable-dgpu-monitoring-label": "Habilitar la monitorización de la GPU dedicada",
|
||||
|
||||
@@ -211,6 +211,10 @@
|
||||
"memory-usage-label": "Utilisation de la mémoire",
|
||||
"network-traffic-description": "Afficher les vitesses de téléchargement et de téléversement du réseau.",
|
||||
"network-traffic-label": "Trafic réseau",
|
||||
"storage-as-percentage-description": "Afficher l'espace disque en pourcentage au lieu des valeurs absolues.",
|
||||
"storage-as-percentage-label": "Disque en pourcentage",
|
||||
"storage-available-description": "Affiche l'espace disque disponible au lieu de l'espace utilisé.",
|
||||
"storage-available-label": "Espace disque disponible",
|
||||
"storage-usage-description": "Afficher les informations d'utilisation de l'espace disque.",
|
||||
"storage-usage-label": "Utilisation du stockage",
|
||||
"swap-usage-description": "Afficher l'utilisation de la mémoire swap.",
|
||||
@@ -272,6 +276,8 @@
|
||||
"label-mode-label": "Mode d'étiquette",
|
||||
"occupied-color-description": "Définir la couleur d'arrière-plan pour les Workspaces occupés.",
|
||||
"occupied-color-label": "Couleur de l'espace de travail occupé",
|
||||
"pill-size-description": "Ajustez la taille des pilules d'espace de travail (50%-100%).",
|
||||
"pill-size-label": "Taille de la pilule",
|
||||
"reverse-scrolling-description": "Inverser la direction du changement d'espace de travail lors du défilement.",
|
||||
"reverse-scrolling-label": "Défilement inversé",
|
||||
"show-applications-description": "Afficher les icônes des applications dans chaque espace de travail.",
|
||||
@@ -387,6 +393,7 @@
|
||||
"disconnecting": "Déconnexion...",
|
||||
"download": "Télécharger",
|
||||
"duration": "Durée",
|
||||
"dysfunctional": "Dysfonctionnel",
|
||||
"edit": "Modifier",
|
||||
"enabled": "Activé",
|
||||
"events": "Événements",
|
||||
@@ -807,6 +814,7 @@
|
||||
"download-title": "Télécharger des jeux de couleurs",
|
||||
"method-description": {
|
||||
"content": "Schéma Material Design avec extraction de couleurs haute fidélité qui correspond étroitement aux couleurs réelles du contenu source.",
|
||||
"dysfunctional": "Comme Faithful, mais choisit la deuxième famille de couleurs la plus dominante comme principale.",
|
||||
"faithful": "Tente de rester proche de la couleur source tout en générant une palette harmonieuse.",
|
||||
"fruit-salad": "Schéma Material Design qui produit une palette ludique et vibrante avec des teintes diverses et variées.",
|
||||
"monochrome": "Schéma Material Design utilisant une échelle de gris à teinte unique avec un contenu chromatique minimal.",
|
||||
@@ -1302,6 +1310,7 @@
|
||||
"sources-placeholder": "Mon dépôt cool",
|
||||
"sources-remove-tooltip": "Supprimer la source du plugin",
|
||||
"title": "Modules d'extension",
|
||||
"translations-reloaded": "Traductions rechargées : {name}",
|
||||
"uninstall-dialog-description": "Êtes-vous sûr de vouloir désinstaller {plugin} ? Cette action supprimera toutes les données du plugin.",
|
||||
"uninstall-dialog-title": "Désinstaller le plugin",
|
||||
"uninstall-error": "Échec de la désinstallation : {error}",
|
||||
@@ -1349,6 +1358,7 @@
|
||||
"system-monitor": {
|
||||
"critical-color-label": "Couleur critique",
|
||||
"custom-highlight-colors-title-label": "Couleurs de surbrillance personnalisées",
|
||||
"disk-available-label": "Espace disque disponible",
|
||||
"disk-section-label": "Utilisation disque",
|
||||
"enable-dgpu-monitoring-description": "Attention : Ceci va activer votre GPU dédié (NVIDIA/AMD), ce qui peut avoir un impact significatif sur l'autonomie de la batterie des ordinateurs portables dotés de cartes graphiques hybrides.",
|
||||
"enable-dgpu-monitoring-label": "Activer la surveillance du GPU dédié",
|
||||
|
||||
@@ -211,6 +211,10 @@
|
||||
"memory-usage-label": "Memóriahasználat",
|
||||
"network-traffic-description": "Hálózati feltöltési és letöltési sebességek megjelenítése.",
|
||||
"network-traffic-label": "Hálózati forgalom",
|
||||
"storage-as-percentage-description": "Lemezterület megjelenítése százalékban abszolút értékek helyett.",
|
||||
"storage-as-percentage-label": "Lemez százalékban",
|
||||
"storage-available-description": "Azt mutatja, mennyi lemezterület áll rendelkezésre, ahelyett, hogy mennyit használnak.",
|
||||
"storage-available-label": "Rendelkezésre álló lemezterület",
|
||||
"storage-usage-description": "Lemezterület-használati információk megjelenítése.",
|
||||
"storage-usage-label": "Tárhelyhasználat",
|
||||
"swap-usage-description": "Cserehely memória használatának megjelenítése.",
|
||||
@@ -272,6 +276,8 @@
|
||||
"label-mode-label": "Címke mód",
|
||||
"occupied-color-description": "A foglalt Workspaces háttérszínének beállítása.",
|
||||
"occupied-color-label": "Foglalt munkaterület színe",
|
||||
"pill-size-description": "Állítsa be a munkaterület-jelzők méretét (50%-100%).",
|
||||
"pill-size-label": "Pill méret",
|
||||
"reverse-scrolling-description": "Görgetéskor fordított irányba váltson a munkaterületek között.",
|
||||
"reverse-scrolling-label": "Görgetés megfordítása",
|
||||
"show-applications-description": "Alkalmazásikonok megjelenítése minden munkaterületen belül.",
|
||||
@@ -387,6 +393,7 @@
|
||||
"disconnecting": "Kapcsolat bontása...",
|
||||
"download": "Letöltés",
|
||||
"duration": "Időtartam",
|
||||
"dysfunctional": "Diszfunkcionális",
|
||||
"edit": "Szerkesztés",
|
||||
"enabled": "Engedélyezve",
|
||||
"events": "Események",
|
||||
@@ -807,6 +814,7 @@
|
||||
"download-title": "Színsémák letöltése",
|
||||
"method-description": {
|
||||
"content": "Material Design séma nagy pontosságú színkivonással, amely szorosan illeszkedik a forrás tartalom tényleges színeihez.",
|
||||
"dysfunctional": "Mint a Faithful, de a második legdominánsabb színcsaládot választja elsődlegesnek.",
|
||||
"faithful": "Igyekszik közel maradni a forrásszínhez, miközben harmonikus palettát generál.",
|
||||
"fruit-salad": "Material Design séma, amely játékos, élénk palettát hoz létre változatos árnyalatokkal.",
|
||||
"monochrome": "Material Design séma egyszínű szürkeskálával, minimális színtartalommal.",
|
||||
@@ -1302,6 +1310,7 @@
|
||||
"sources-placeholder": "Saját menő tárolóm",
|
||||
"sources-remove-tooltip": "Bővítményforrás eltávolítása",
|
||||
"title": "Bővítmények",
|
||||
"translations-reloaded": "Fordítások újratöltve: {name}",
|
||||
"uninstall-dialog-description": "Biztosan el szeretné távolítani a(z) {plugin} bővítményt? Ez eltávolítja az összes bővítményadatot.",
|
||||
"uninstall-dialog-title": "Bővítmény eltávolítása",
|
||||
"uninstall-error": "Sikertelen eltávolítás: {error}",
|
||||
@@ -1349,6 +1358,7 @@
|
||||
"system-monitor": {
|
||||
"critical-color-label": "Kritikus szín",
|
||||
"custom-highlight-colors-title-label": "Egyéni kiemelőszínek",
|
||||
"disk-available-label": "Rendelkezésre álló lemezterület",
|
||||
"disk-section-label": "Lemezhasználat",
|
||||
"enable-dgpu-monitoring-description": "Figyelem: Ez felébreszti a dedikált GPU-t (NVIDIA/AMD), ami jelentősen befolyásolhatja az akkumulátor élettartamát a hibrid grafikával rendelkező laptopokon.",
|
||||
"enable-dgpu-monitoring-label": "Dedikált GPU figyelés engedélyezése",
|
||||
|
||||
@@ -211,6 +211,10 @@
|
||||
"memory-usage-label": "メモリ使用量",
|
||||
"network-traffic-description": "ネットワークのアップロード・ダウンロード速度を表示します。",
|
||||
"network-traffic-label": "ネットワークトラフィック",
|
||||
"storage-as-percentage-description": "ディスク容量を絶対値ではなくパーセンテージで表示する。",
|
||||
"storage-as-percentage-label": "ディスクをパーセンテージで",
|
||||
"storage-available-description": "使用量ではなく、利用可能なディスク容量を表示します。",
|
||||
"storage-available-label": "利用可能なディスク容量",
|
||||
"storage-usage-description": "ストレージの使用状況を表示します。",
|
||||
"storage-usage-label": "ストレージ使用量",
|
||||
"swap-usage-description": "スワップメモリの使用状況を表示します。",
|
||||
@@ -272,6 +276,8 @@
|
||||
"label-mode-label": "ラベルモード",
|
||||
"occupied-color-description": "使用中のWorkspaceの背景色を設定します。",
|
||||
"occupied-color-label": "使用中のワークスペースの色",
|
||||
"pill-size-description": "ワークスペースピルのサイズを調整します (50%-100%)。",
|
||||
"pill-size-label": "ピルサイズ",
|
||||
"reverse-scrolling-description": "スクロール時のワークスペース切り替え方向を反転する。",
|
||||
"reverse-scrolling-label": "スクロール方向を反転",
|
||||
"show-applications-description": "各ワークスペース内にアプリアイコンを表示します。",
|
||||
@@ -387,6 +393,7 @@
|
||||
"disconnecting": "切断中...",
|
||||
"download": "ダウンロード",
|
||||
"duration": "期間",
|
||||
"dysfunctional": "機能不全",
|
||||
"edit": "編集",
|
||||
"enabled": "有効",
|
||||
"events": "イベント",
|
||||
@@ -807,6 +814,7 @@
|
||||
"download-title": "配色のダウンロード",
|
||||
"method-description": {
|
||||
"content": "ソースコンテンツの実際の色と厳密に一致する、高忠実度の色抽出を備えたMaterial Designスキーム。",
|
||||
"dysfunctional": "Faithfulのように、2番目に支配的な色族をプライマリとして選択します。",
|
||||
"faithful": "調和のとれたパレットを生成しながら、ソースカラーにできるだけ近い色を維持しようとします。",
|
||||
"fruit-salad": "多様で多彩な色合いを持つ、遊び心のある鮮やかなパレットを生み出すMaterial Designスキーム。",
|
||||
"monochrome": "単一色相のグレースケールと最小限の彩度コンテンツを使用した Material Design スキーム。",
|
||||
@@ -1302,6 +1310,7 @@
|
||||
"sources-placeholder": "マイリポジトリ",
|
||||
"sources-remove-tooltip": "プラグインソースを削除",
|
||||
"title": "プラグイン",
|
||||
"translations-reloaded": "翻訳をリロードしました: {name}",
|
||||
"uninstall-dialog-description": "本当に {plugin} をアンインストールしますか?すべてのプラグインデータが削除されます。",
|
||||
"uninstall-dialog-title": "プラグインのアンインストール",
|
||||
"uninstall-error": "アンインストールに失敗しました: {error}",
|
||||
@@ -1349,6 +1358,7 @@
|
||||
"system-monitor": {
|
||||
"critical-color-label": "危険時の色",
|
||||
"custom-highlight-colors-title-label": "カスタムハイライト色",
|
||||
"disk-available-label": "利用可能なディスク容量",
|
||||
"disk-section-label": "ストレージ使用量",
|
||||
"enable-dgpu-monitoring-description": "警告: これにより外部GPU (NVIDIA/AMD) が起動状態になるため、ハイブリッドグラフィックス搭載のノート PC ではバッテリー駆動時間に大きな影響を与える可能性があります。",
|
||||
"enable-dgpu-monitoring-label": "外部GPUモニタリングを有効にする",
|
||||
|
||||
@@ -211,6 +211,10 @@
|
||||
"memory-usage-label": "메모리 사용량",
|
||||
"network-traffic-description": "네트워크 업로드 및 다운로드 속도를 표시합니다.",
|
||||
"network-traffic-label": "네트워크 트래픽",
|
||||
"storage-as-percentage-description": "디스크 공간을 절대값 대신 백분율로 표시합니다.",
|
||||
"storage-as-percentage-label": "디스크 백분율",
|
||||
"storage-available-description": "사용된 공간 대신 사용 가능한 디스크 공간을 표시합니다.",
|
||||
"storage-available-label": "사용 가능한 디스크 공간",
|
||||
"storage-usage-description": "디스크 공간 사용량 정보를 표시합니다.",
|
||||
"storage-usage-label": "저장소 사용량",
|
||||
"swap-usage-description": "스왑 메모리 사용량을 표시합니다.",
|
||||
@@ -272,6 +276,8 @@
|
||||
"label-mode-label": "레이블 모드",
|
||||
"occupied-color-description": "사용 중인 작업 공간의 배경색을 설정합니다.",
|
||||
"occupied-color-label": "사용 중인 작업 공간 색상",
|
||||
"pill-size-description": "작업 공간 알약의 크기를 조절합니다 (50%-100%).",
|
||||
"pill-size-label": "필 크기",
|
||||
"reverse-scrolling-description": "스크롤할 때 작업 공간 전환 방향을 반대로 합니다.",
|
||||
"reverse-scrolling-label": "스크롤 반전",
|
||||
"show-applications-description": "각 작업 공간 내부에 애플리케이션 아이콘을 표시합니다.",
|
||||
@@ -387,6 +393,7 @@
|
||||
"disconnecting": "연결 해제 중...",
|
||||
"download": "다운로드",
|
||||
"duration": "지속 시간",
|
||||
"dysfunctional": "기능 장애",
|
||||
"edit": "편집",
|
||||
"enabled": "활성화됨",
|
||||
"events": "일정",
|
||||
@@ -807,6 +814,7 @@
|
||||
"download-title": "색상 구성 다운로드",
|
||||
"method-description": {
|
||||
"content": "소스 콘텐츠의 실제 색상을 매우 유사하게 재현하는 고품질 색상 추출을 사용하는 머티리얼 디자인 구성입니다.",
|
||||
"dysfunctional": "Faithful과 비슷하지만, 두 번째로 지배적인 색상 계열을 기본으로 선택합니다.",
|
||||
"faithful": "소스 색상에 가깝게 유지하면서 조화로운 팔레트 생성을 시도합니다.",
|
||||
"fruit-salad": "다양하고 다채로운 색조로 유쾌하고 생동감 넘치는 팔레트를 생성하는 머티리얼 디자인 구성입니다.",
|
||||
"monochrome": "채도가 최소화된 단일 색조 그레이스케일을 사용하는 머티리얼 디자인 구성입니다.",
|
||||
@@ -1302,6 +1310,7 @@
|
||||
"sources-placeholder": "내 멋진 저장소",
|
||||
"sources-remove-tooltip": "플러그인 소스 제거",
|
||||
"title": "플러그인",
|
||||
"translations-reloaded": "번역 다시 로드됨: {name}",
|
||||
"uninstall-dialog-description": "정말 {plugin}을(를) 제거하시겠습니까? 모든 플러그인 데이터가 삭제됩니다.",
|
||||
"uninstall-dialog-title": "플러그인 제거",
|
||||
"uninstall-error": "제거 실패: {error}",
|
||||
@@ -1349,6 +1358,7 @@
|
||||
"system-monitor": {
|
||||
"critical-color-label": "위험 색상",
|
||||
"custom-highlight-colors-title-label": "사용자 지정 강조 색상",
|
||||
"disk-available-label": "사용 가능한 디스크 공간",
|
||||
"disk-section-label": "디스크 사용량",
|
||||
"enable-dgpu-monitoring-description": "경고: 이 기능은 외장 GPU(NVIDIA/AMD)를 깨워 하이브리드 그래픽 노트북의 배터리 수명에 상당한 영향을 줄 수 있습니다.",
|
||||
"enable-dgpu-monitoring-label": "외장 GPU 모니터링 활성화",
|
||||
|
||||
@@ -211,6 +211,10 @@
|
||||
"memory-usage-label": "Geheugengebruik",
|
||||
"network-traffic-description": "Toon uploadsnelheid en downloadsnelheid van het netwerk.",
|
||||
"network-traffic-label": "Netwerkverkeer",
|
||||
"storage-as-percentage-description": "Toon schijfruimte als percentage in plaats van absolute waarden.",
|
||||
"storage-as-percentage-label": "Schijf als percentage",
|
||||
"storage-available-description": "Toont hoeveel schijfruimte beschikbaar is in plaats van hoeveel er wordt gebruikt.",
|
||||
"storage-available-label": "Beschikbare schijfruimte",
|
||||
"storage-usage-description": "Toon informatie over schijfruimtegebruik.",
|
||||
"storage-usage-label": "Opslaggebruik",
|
||||
"swap-usage-description": "Toon swap geheugengebruik.",
|
||||
@@ -272,6 +276,8 @@
|
||||
"label-mode-label": "Labelmodus",
|
||||
"occupied-color-description": "Stel de achtergrondkleur in voor bezette Workspaces.",
|
||||
"occupied-color-label": "Kleur van bezette workspace",
|
||||
"pill-size-description": "Pas de grootte van de werkruimte-pillen aan (50%-100%).",
|
||||
"pill-size-label": "Pilgrootte",
|
||||
"reverse-scrolling-description": "Keer de richting van het wisselen van werkruimtes om bij het scrollen.",
|
||||
"reverse-scrolling-label": "Omgekeerd scrollen",
|
||||
"show-applications-description": "Toon applicatiepictogrammen in elke werkruimte.",
|
||||
@@ -387,6 +393,7 @@
|
||||
"disconnecting": "Verbinding verbroken...",
|
||||
"download": "Downloaden",
|
||||
"duration": "Duur",
|
||||
"dysfunctional": "Dysfunctioneel",
|
||||
"edit": "Bewerken",
|
||||
"enabled": "Ingeschakeld",
|
||||
"events": "Evenementen",
|
||||
@@ -807,6 +814,7 @@
|
||||
"download-title": "Kleurenschema's downloaden",
|
||||
"method-description": {
|
||||
"content": "Material Design-schema met high-fidelity kleurextractie die nauw aansluit bij de werkelijke kleuren van de broninhoud.",
|
||||
"dysfunctional": "Zoals Faithful, maar kiest de op één na meest dominante kleurenfamilie als primair.",
|
||||
"faithful": "Probeert dicht bij de bronkleur te blijven en tegelijkertijd een harmonieus palet te genereren.",
|
||||
"fruit-salad": "Material Design-schema dat een speels, levendig palet produceert met diverse en gevarieerde tinten.",
|
||||
"monochrome": "Material Design-schema met een grijswaarde met één tint en minimale chromatische inhoud.",
|
||||
@@ -1302,6 +1310,7 @@
|
||||
"sources-placeholder": "Mijn coole repository",
|
||||
"sources-remove-tooltip": "Pluginbron verwijderen",
|
||||
"title": "Plugins",
|
||||
"translations-reloaded": "Vertalingen opnieuw geladen: {name}",
|
||||
"uninstall-dialog-description": "Weet je zeker dat je {plugin} wilt verwijderen? Hiermee worden alle plugin-gegevens verwijderd.",
|
||||
"uninstall-dialog-title": "Plugin verwijderen",
|
||||
"uninstall-error": "Verwijderen mislukt: {error}",
|
||||
@@ -1349,6 +1358,7 @@
|
||||
"system-monitor": {
|
||||
"critical-color-label": "Kritische kleur",
|
||||
"custom-highlight-colors-title-label": "Aangepaste markeerkleuren",
|
||||
"disk-available-label": "Beschikbare schijfruimte",
|
||||
"disk-section-label": "Schijfgebruik",
|
||||
"enable-dgpu-monitoring-description": "Waarschuwing: Dit zal uw dedicated GPU (NVIDIA/AMD) activeren, wat een aanzienlijke impact kan hebben op de accuduur van laptops met hybride grafische kaarten.",
|
||||
"enable-dgpu-monitoring-label": "Dedicated GPU-monitoring inschakelen",
|
||||
|
||||
@@ -211,6 +211,10 @@
|
||||
"memory-usage-label": "Użycie pamięci",
|
||||
"network-traffic-description": "Wyświetl prędkość wysyłania i pobierania danych w sieci.",
|
||||
"network-traffic-label": "Ruch sieciowy",
|
||||
"storage-as-percentage-description": "Wyświetlaj miejsce na dysku jako procent zamiast wartości bezwzględnych.",
|
||||
"storage-as-percentage-label": "Dysk jako procent",
|
||||
"storage-available-description": "Pokazuje, ile miejsca na dysku jest dostępne, zamiast ile jest używane.",
|
||||
"storage-available-label": "Dostępne miejsce na dysku",
|
||||
"storage-usage-description": "Pokaż informacje o użyciu miejsca na dysku.",
|
||||
"storage-usage-label": "Użycie dysku",
|
||||
"swap-usage-description": "Pokaż użycie pamięci wymiany.",
|
||||
@@ -272,6 +276,8 @@
|
||||
"label-mode-label": "Tryb etykiet",
|
||||
"occupied-color-description": "Ustaw kolor tła dla zajętych Workspace'ów.",
|
||||
"occupied-color-label": "Kolor zajętego obszaru roboczego",
|
||||
"pill-size-description": "Dostosuj rozmiar pigułek obszaru roboczego (50%-100%).",
|
||||
"pill-size-label": "Rozmiar pigułki",
|
||||
"reverse-scrolling-description": "Odwróć kierunek przełączania obszarów roboczych podczas przewijania.",
|
||||
"reverse-scrolling-label": "Odwróć przewijanie",
|
||||
"show-applications-description": "Wyświetl ikony aplikacji wewnątrz każdego obszaru roboczego.",
|
||||
@@ -387,6 +393,7 @@
|
||||
"disconnecting": "Rozłączanie...",
|
||||
"download": "Pobierz",
|
||||
"duration": "Czas trwania",
|
||||
"dysfunctional": "Dysfunkcyjny",
|
||||
"edit": "Edytuj",
|
||||
"enabled": "Włączone",
|
||||
"events": "Wydarzenia",
|
||||
@@ -807,6 +814,7 @@
|
||||
"download-title": "Pobierz schematy kolorów",
|
||||
"method-description": {
|
||||
"content": "Schemat Material Design z ekstrakcją kolorów o wysokiej wierności, która ściśle odpowiada rzeczywistym kolorom treści źródłowej.",
|
||||
"dysfunctional": "Jak Faithful, ale wybiera drugą najbardziej dominującą rodzinę kolorów jako główną.",
|
||||
"faithful": "Próbuje pozostać blisko koloru źródłowego, jednocześnie generując harmonijną paletę.",
|
||||
"fruit-salad": "Schemat Material Design, który tworzy radosną, żywą paletę z różnorodnymi odcieniami.",
|
||||
"monochrome": "Schemat Material Design wykorzystujący jednobarwną skalę szarości z minimalną zawartością chromatyczną.",
|
||||
@@ -1302,6 +1310,7 @@
|
||||
"sources-placeholder": "Moje fajne repozytorium",
|
||||
"sources-remove-tooltip": "Usuń źródło wtyczek",
|
||||
"title": "Wtyczki",
|
||||
"translations-reloaded": "Przeładowano tłumaczenia: {name}",
|
||||
"uninstall-dialog-description": "Czy na pewno chcesz odinstalować {plugin}? Spowoduje to usunięcie wszystkich danych wtyczki.",
|
||||
"uninstall-dialog-title": "Odinstaluj wtyczkę",
|
||||
"uninstall-error": "Błąd odinstalowywania: {error}",
|
||||
@@ -1349,6 +1358,7 @@
|
||||
"system-monitor": {
|
||||
"critical-color-label": "Kolor krytyczny",
|
||||
"custom-highlight-colors-title-label": "Własne kolory podświetlenia",
|
||||
"disk-available-label": "Dostępne miejsce na dysku",
|
||||
"disk-section-label": "Użycie dysku",
|
||||
"enable-dgpu-monitoring-description": "Ostrzeżenie: To obudzi twoją dedykowaną kartę graficzną (NVIDIA/AMD), co może znacząco wpłynąć na żywotność baterii w laptopach z hybrydową grafiką.",
|
||||
"enable-dgpu-monitoring-label": "Włącz monitorowanie dedykowanej karty graficznej",
|
||||
|
||||
@@ -211,6 +211,10 @@
|
||||
"memory-usage-label": "Uso de memória",
|
||||
"network-traffic-description": "Exibir as velocidades de upload e download da rede.",
|
||||
"network-traffic-label": "Tráfego de rede",
|
||||
"storage-as-percentage-description": "Mostrar o espaço em disco como percentagem em vez de valores absolutos.",
|
||||
"storage-as-percentage-label": "Disco como percentagem",
|
||||
"storage-available-description": "Mostra quanto espaço em disco está disponível em vez de quanto é usado.",
|
||||
"storage-available-label": "Espaço em disco disponível",
|
||||
"storage-usage-description": "Mostrar as informações de uso do espaço em disco.",
|
||||
"storage-usage-label": "Uso de armazenamento",
|
||||
"swap-usage-description": "Mostrar o uso da memória swap.",
|
||||
@@ -272,6 +276,8 @@
|
||||
"label-mode-label": "Modo de rótulo",
|
||||
"occupied-color-description": "Definir a cor de fundo para Workspaces ocupados.",
|
||||
"occupied-color-label": "Cor do espaço de trabalho ocupado",
|
||||
"pill-size-description": "Ajuste o tamanho das pílulas da área de trabalho (50%-100%).",
|
||||
"pill-size-label": "Tamanho da pílula",
|
||||
"reverse-scrolling-description": "Inverter a direção da troca de áreas de trabalho ao rolar.",
|
||||
"reverse-scrolling-label": "Rolagem invertida",
|
||||
"show-applications-description": "Exibir ícones de aplicativos dentro de cada espaço de trabalho.",
|
||||
@@ -387,6 +393,7 @@
|
||||
"disconnecting": "Desconectando...",
|
||||
"download": "Baixar",
|
||||
"duration": "Duração",
|
||||
"dysfunctional": "Disfuncional",
|
||||
"edit": "Editar",
|
||||
"enabled": "Ativado",
|
||||
"events": "Eventos",
|
||||
@@ -807,6 +814,7 @@
|
||||
"download-title": "Baixar esquemas de cores",
|
||||
"method-description": {
|
||||
"content": "Esquema de Material Design com extração de cores de alta fidelidade que corresponde de perto às cores reais do conteúdo de origem.",
|
||||
"dysfunctional": "Como Faithful, mas escolhe a segunda família de cores mais dominante como primária.",
|
||||
"faithful": "Tenta manter-se próximo da cor de origem, enquanto gera uma paleta harmoniosa.",
|
||||
"fruit-salad": "Esquema de Material Design que produz uma paleta divertida e vibrante com tons diversos e variados.",
|
||||
"monochrome": "Esquema de Material Design usando uma escala de cinza de tom único com conteúdo cromático mínimo.",
|
||||
@@ -1302,6 +1310,7 @@
|
||||
"sources-placeholder": "Meu repositório legal",
|
||||
"sources-remove-tooltip": "Remover código fonte do plugin",
|
||||
"title": "Plugins",
|
||||
"translations-reloaded": "Traduções recarregadas: {name}",
|
||||
"uninstall-dialog-description": "Tem certeza de que deseja desinstalar {plugin}? Isso removerá todos os dados do plugin.",
|
||||
"uninstall-dialog-title": "Desinstalar plugin",
|
||||
"uninstall-error": "Falha ao desinstalar: {error}",
|
||||
@@ -1349,6 +1358,7 @@
|
||||
"system-monitor": {
|
||||
"critical-color-label": "Cor crítica",
|
||||
"custom-highlight-colors-title-label": "Cores de destaque personalizadas",
|
||||
"disk-available-label": "Espaço em disco disponível",
|
||||
"disk-section-label": "Uso do disco",
|
||||
"enable-dgpu-monitoring-description": "Atenção: Isto irá ativar sua GPU dedicada (NVIDIA/AMD), o que pode impactar significativamente a duração da bateria em laptops com gráficos híbridos.",
|
||||
"enable-dgpu-monitoring-label": "Ativar o monitoramento da GPU dedicada",
|
||||
|
||||
@@ -211,6 +211,10 @@
|
||||
"memory-usage-label": "Использование памяти",
|
||||
"network-traffic-description": "Отображать скорости загрузки и скачивания по сети.",
|
||||
"network-traffic-label": "Сетевой трафик",
|
||||
"storage-as-percentage-description": "Показывать место на диске в процентах вместо абсолютных значений.",
|
||||
"storage-as-percentage-label": "Диск в процентах",
|
||||
"storage-available-description": "Показывает, сколько места на диске доступно, вместо того, сколько используется.",
|
||||
"storage-available-label": "Доступное место на диске",
|
||||
"storage-usage-description": "Показывать информацию об использовании дискового пространства.",
|
||||
"storage-usage-label": "Использование хранилища",
|
||||
"swap-usage-description": "Показать использование swap-памяти.",
|
||||
@@ -272,6 +276,8 @@
|
||||
"label-mode-label": "Режим метки",
|
||||
"occupied-color-description": "Установить цвет фона для занятых рабочих пространств.",
|
||||
"occupied-color-label": "Цвет занятого рабочего пространства",
|
||||
"pill-size-description": "Отрегулируйте размер индикаторов рабочих пространств (50%-100%).",
|
||||
"pill-size-label": "Размер капсулы",
|
||||
"reverse-scrolling-description": "Изменить направление переключения рабочих пространств при прокрутке.",
|
||||
"reverse-scrolling-label": "Обратная прокрутка",
|
||||
"show-applications-description": "Отображать значки приложений внутри каждого рабочего пространства.",
|
||||
@@ -387,6 +393,7 @@
|
||||
"disconnecting": "Отключение...",
|
||||
"download": "Скачать",
|
||||
"duration": "Продолжительность",
|
||||
"dysfunctional": "Дисфункциональный",
|
||||
"edit": "Редактировать",
|
||||
"enabled": "Включено",
|
||||
"events": "События",
|
||||
@@ -807,6 +814,7 @@
|
||||
"download-title": "Загрузить цветовые схемы",
|
||||
"method-description": {
|
||||
"content": "Схема Material Design с высокоточной экстракцией цветов, которая точно соответствует фактическим цветам исходного контента.",
|
||||
"dysfunctional": "Как Faithful, но выбирает второе по доминантности цветовое семейство в качестве основного.",
|
||||
"faithful": "Пытается оставаться близким к исходному цвету, создавая при этом гармоничную палитру.",
|
||||
"fruit-salad": "Схема Material Design, создающая игривую, яркую палитру с разнообразными оттенками.",
|
||||
"monochrome": "Схема Material Design, использующая однотонную шкалу серого с минимальным хроматическим содержанием.",
|
||||
@@ -1302,6 +1310,7 @@
|
||||
"sources-placeholder": "Мой крутой репозиторий",
|
||||
"sources-remove-tooltip": "Удалить исходный код плагина",
|
||||
"title": "Плагины",
|
||||
"translations-reloaded": "Переводы перезагружены: {name}",
|
||||
"uninstall-dialog-description": "Вы уверены, что хотите удалить {plugin}? Это удалит все данные плагина.",
|
||||
"uninstall-dialog-title": "Удалить плагин",
|
||||
"uninstall-error": "Не удалось удалить: {error}",
|
||||
@@ -1349,6 +1358,7 @@
|
||||
"system-monitor": {
|
||||
"critical-color-label": "Критический цвет",
|
||||
"custom-highlight-colors-title-label": "Пользовательские цвета выделения",
|
||||
"disk-available-label": "Доступно места на диске",
|
||||
"disk-section-label": "Использование диска",
|
||||
"enable-dgpu-monitoring-description": "Внимание: Это разбудит ваш дискретный графический процессор (NVIDIA/AMD), что может значительно повлиять на время работы от аккумулятора на ноутбуках с гибридной графикой.",
|
||||
"enable-dgpu-monitoring-label": "Включить мониторинг дискретного GPU",
|
||||
@@ -1416,17 +1426,17 @@
|
||||
"automation-change-mode-label": "Режим смены",
|
||||
"automation-custom-interval-description": "Введите время в формате ЧЧ:ММ (например, 01:30).",
|
||||
"automation-custom-interval-label": "Пользовательский интервал",
|
||||
"automation-interval-description": "Как часто автоматически менять обои.",
|
||||
"automation-interval-description": "Частота автоматической смены обоев.",
|
||||
"automation-interval-label": "Интервал смены обоев",
|
||||
"automation-random-wallpaper-description": "Запланировать смену случайных обоев через регулярные интервалы.",
|
||||
"automation-scheduled-change-description": "Автоматически менять обои через регулярные интервалы.",
|
||||
"automation-scheduled-change-description": "Автоматически менять обои через регулярные интервалы времени.",
|
||||
"automation-scheduled-change-label": "Запланированная смена",
|
||||
"look-feel-edge-smoothness-description": "Применяет мягкий, растушёванный эффект к краю переходов.",
|
||||
"look-feel-edge-smoothness-label": "Смягчить край перехода",
|
||||
"look-feel-fill-color-description": "Выберите цвет заливки, который может появиться за обоями.",
|
||||
"look-feel-fill-mode-description": "Выберите, как изображение должно масштабироваться, чтобы соответствовать разрешению вашего монитора.",
|
||||
"look-feel-fill-mode-label": "Режим заполнения",
|
||||
"look-feel-title": "Внешний вид и ощущения",
|
||||
"look-feel-title": "Внешний вид",
|
||||
"look-feel-transition-duration-description": "Продолжительность анимации перехода в секундах.",
|
||||
"look-feel-transition-duration-label": "Продолжительность перехода",
|
||||
"look-feel-transition-type-description": "Тип анимации при переключении между обоями.",
|
||||
@@ -1446,9 +1456,9 @@
|
||||
"settings-recursive-search-description": "Также искать обои во вложенных папках каталога обоев.",
|
||||
"settings-recursive-search-label": "Искать во вложенных папках",
|
||||
"settings-select-monitor-folder": "Выбрать папку с обоями для монитора",
|
||||
"settings-selector-description": "Выберите обои.",
|
||||
"settings-selector-description": "Перейти на панель выбора обоев.",
|
||||
"settings-selector-position-description": "Выберите, где появляется панель выбора обоев.",
|
||||
"settings-show-hidden-files-tooltip-hide": "Скрыть скрытые файлы",
|
||||
"settings-show-hidden-files-tooltip-hide": "Не показывать скрытые файлы",
|
||||
"settings-show-hidden-files-tooltip-show": "Показать скрытые файлы",
|
||||
"settings-title": "Настройки обоев",
|
||||
"settings-view-mode-description": "Выберите способ отображения обоев из вашей директории.",
|
||||
|
||||
@@ -211,6 +211,10 @@
|
||||
"memory-usage-label": "Minnesanvändning",
|
||||
"network-traffic-description": "Visa nätverkets uppladdnings- och nedladdningshastigheter.",
|
||||
"network-traffic-label": "Nätverkstrafik",
|
||||
"storage-as-percentage-description": "Visa diskutrymme som procentandel istället för absoluta värden.",
|
||||
"storage-as-percentage-label": "Disk i procent",
|
||||
"storage-available-description": "Visar hur mycket diskutrymme som är tillgängligt istället för hur mycket som används.",
|
||||
"storage-available-label": "Tillgängligt diskutrymme",
|
||||
"storage-usage-description": "Visa information om diskutrymmesanvändning.",
|
||||
"storage-usage-label": "Lagringsanvändning",
|
||||
"swap-usage-description": "Visa användning av växlingsminne.",
|
||||
@@ -272,6 +276,8 @@
|
||||
"label-mode-label": "Etikettläge",
|
||||
"occupied-color-description": "Ställ in bakgrundsfärgen för använda arbetsytor.",
|
||||
"occupied-color-label": "Färg för använda arbetsytor",
|
||||
"pill-size-description": "Justera storleken på arbetsytepillerna (50%-100%).",
|
||||
"pill-size-label": "Pillerstorlek",
|
||||
"reverse-scrolling-description": "Vänd riktningen för arbetsyteväxling vid rullning.",
|
||||
"reverse-scrolling-label": "Omvänd rullning",
|
||||
"show-applications-description": "Visa applikationsikoner i varje arbetsyta.",
|
||||
@@ -387,6 +393,7 @@
|
||||
"disconnecting": "Kopplar från...",
|
||||
"download": "Hämta ner",
|
||||
"duration": "Varaktighet",
|
||||
"dysfunctional": "Dysfunktionell",
|
||||
"edit": "Redigera",
|
||||
"enabled": "Aktiverad",
|
||||
"events": "Händelser",
|
||||
@@ -807,6 +814,7 @@
|
||||
"download-title": "Hämta ner färgscheman",
|
||||
"method-description": {
|
||||
"content": "Material Design-schema med högkvalitativ färgutvinning som nära matchar källinnehållets faktiska färger.",
|
||||
"dysfunctional": "Som Faithful, men väljer den näst mest dominanta färgfamiljen som primär.",
|
||||
"faithful": "Försöker hålla sig nära källfärgen samtidigt som en harmonisk palett genereras.",
|
||||
"fruit-salad": "Material Design-schema som ger en lekfull, livfull palett med olika och varierande nyanser.",
|
||||
"monochrome": "Material Design-schema som använder en enda nyans i gråskala med minimalt kromatiskt innehåll.",
|
||||
@@ -1143,6 +1151,8 @@
|
||||
"weather-show-in-calendar-label": "Visa väder i kalendern"
|
||||
},
|
||||
"lock-screen": {
|
||||
"allow-password-with-fprintd-description": "När fprintd (fingeravtrycksautentisering) är aktivt, låter det här alternativet dig fortfarande logga in med ditt lösenord istället för ett fingeravtryck",
|
||||
"allow-password-with-fprintd-label": "Tillåt lösenordsinloggning med fprintd",
|
||||
"auto-start-auth-description": "t.ex. startar automatiskt fingeravtrycksautentisering utan att du behöver trycka på en tangent eller knapp.",
|
||||
"auto-start-auth-label": "Automatisk start av autentisering",
|
||||
"compact-lockscreen-description": "Visa endast inloggningsfältet och systemkontrollerna, dölj väder- och mediewidgets.",
|
||||
@@ -1300,6 +1310,7 @@
|
||||
"sources-placeholder": "Mitt coola arkiv",
|
||||
"sources-remove-tooltip": "Ta bort insticksmodulkälla",
|
||||
"title": "Insticksmoduler",
|
||||
"translations-reloaded": "Översättningar omladdade: {name}",
|
||||
"uninstall-dialog-description": "Är du säker på att du vill avinstallera {plugin}? Detta kommer att ta bort all insticksmoduldata.",
|
||||
"uninstall-dialog-title": "Avinstallera insticksmodul",
|
||||
"uninstall-error": "Avinstallationen misslyckades: {error}",
|
||||
@@ -1347,6 +1358,7 @@
|
||||
"system-monitor": {
|
||||
"critical-color-label": "Kritisk färg",
|
||||
"custom-highlight-colors-title-label": "Anpassade markeringsfärger",
|
||||
"disk-available-label": "Tillgängligt diskutrymme",
|
||||
"disk-section-label": "Diskanvändning",
|
||||
"enable-dgpu-monitoring-description": "Varning: Detta aktiverar din diskreta GPU (NVIDIA/AMD), vilket kan påverka batteritiden avsevärt på bärbara datorer med hybridgrafik.",
|
||||
"enable-dgpu-monitoring-label": "Aktivera övervakning av diskret GPU",
|
||||
|
||||
@@ -211,6 +211,10 @@
|
||||
"memory-usage-label": "Bellek kullanımı",
|
||||
"network-traffic-description": "Ağ yükleme ve indirme hızlarını göster.",
|
||||
"network-traffic-label": "Ağ trafiği",
|
||||
"storage-as-percentage-description": "Disk alanını mutlak değerler yerine yüzde olarak göster.",
|
||||
"storage-as-percentage-label": "Disk yüzde olarak",
|
||||
"storage-available-description": "Kullanılan miktar yerine ne kadar disk alanı olduğunu gösterir.",
|
||||
"storage-available-label": "Kullanılabilir disk alanı",
|
||||
"storage-usage-description": "Disk alanı kullanım bilgilerini göster.",
|
||||
"storage-usage-label": "Depolama kullanımı",
|
||||
"swap-usage-description": "Takas belleği kullanımını göster.",
|
||||
@@ -272,6 +276,8 @@
|
||||
"label-mode-label": "Etiket Modu",
|
||||
"occupied-color-description": "Dolu Workspaces için arka plan rengini ayarla.",
|
||||
"occupied-color-label": "Dolu çalışma alanı rengi",
|
||||
"pill-size-description": "Çalışma alanı haplarının boyutunu ayarlayın (50%-100%).",
|
||||
"pill-size-label": "Kapsül boyutu",
|
||||
"reverse-scrolling-description": "Kaydırırken çalışma alanı geçiş yönünü tersine çevir.",
|
||||
"reverse-scrolling-label": "Ters kaydırma",
|
||||
"show-applications-description": "Her çalışma alanının içinde uygulama simgelerini görüntüle.",
|
||||
@@ -387,6 +393,7 @@
|
||||
"disconnecting": "Bağlantı kesiliyor...",
|
||||
"download": "İndir",
|
||||
"duration": "Süre",
|
||||
"dysfunctional": "İşlevsiz",
|
||||
"edit": "Düzenle",
|
||||
"enabled": "Etkinleştirildi",
|
||||
"events": "Etkinlikler",
|
||||
@@ -807,6 +814,7 @@
|
||||
"download-title": "Renk şemalarını indir",
|
||||
"method-description": {
|
||||
"content": "Kaynak içeriğin gerçek renkleriyle yakından eşleşen, yüksek doğruluklu renk çıkarma özelliğine sahip Material Design şeması.",
|
||||
"dysfunctional": "Faithful gibi, ancak ikinci en baskın renk ailesini birincil olarak seçer.",
|
||||
"faithful": "Uyumlu bir palet oluştururken kaynak rengine yakın kalmaya çalışır.",
|
||||
"fruit-salad": "Çeşitli ve farklı tonlara sahip, eğlenceli, canlı bir palet üreten Material Design şeması.",
|
||||
"monochrome": "Minimal kromatik içerikli tek tonlu gri tonlamalı Material Design şeması.",
|
||||
@@ -1302,6 +1310,7 @@
|
||||
"sources-placeholder": "Havalı depom",
|
||||
"sources-remove-tooltip": "Eklenti kaynağını kaldır",
|
||||
"title": "Eklentiler",
|
||||
"translations-reloaded": "Çeviriler yeniden yüklendi: {name}",
|
||||
"uninstall-dialog-description": "{plugin} eklentisini kaldırmak istediğinizden emin misiniz? Bu, tüm eklenti verilerini kaldıracaktır.",
|
||||
"uninstall-dialog-title": "Eklentiyi kaldır",
|
||||
"uninstall-error": "Kaldırma başarısız oldu: {error}",
|
||||
@@ -1349,6 +1358,7 @@
|
||||
"system-monitor": {
|
||||
"critical-color-label": "Kritik renk",
|
||||
"custom-highlight-colors-title-label": "Özel vurgulama renkleri",
|
||||
"disk-available-label": "Kullanılabilir disk alanı",
|
||||
"disk-section-label": "Disk kullanımı",
|
||||
"enable-dgpu-monitoring-description": "Uyarı: Bu, ayrık GPU'nuzu (NVIDIA/AMD) uyandıracak ve bu da hibrit grafiklere sahip dizüstü bilgisayarlarda pil ömrünü önemli ölçüde etkileyebilir.",
|
||||
"enable-dgpu-monitoring-label": "Ayrık GPU izlemeyi etkinleştir",
|
||||
|
||||
@@ -211,6 +211,10 @@
|
||||
"memory-usage-label": "Використання пам'яті",
|
||||
"network-traffic-description": "Відображати швидкість прийому та передачі даних.",
|
||||
"network-traffic-label": "Мережевий трафік",
|
||||
"storage-as-percentage-description": "Показувати місце на диску у відсотках замість абсолютних значень.",
|
||||
"storage-as-percentage-label": "Диск у відсотках",
|
||||
"storage-available-description": "Показує, скільки місця на диску доступно, замість того, скільки використовується.",
|
||||
"storage-available-label": "Доступне місце на диску",
|
||||
"storage-usage-description": "Показувати інформацію про використання дискового простору.",
|
||||
"storage-usage-label": "Використання сховища",
|
||||
"swap-usage-description": "Показати використання swap-пам'яті.",
|
||||
@@ -272,6 +276,8 @@
|
||||
"label-mode-label": "Режим міток",
|
||||
"occupied-color-description": "Встановити колір фону для зайнятих Workspace.",
|
||||
"occupied-color-label": "Колір зайнятого робочого столу",
|
||||
"pill-size-description": "Налаштуйте розмір пігулок робочого простору (50%-100%).",
|
||||
"pill-size-label": "Розмір капсули",
|
||||
"reverse-scrolling-description": "Змінити напрямок перемикання робочих просторів під час прокручування.",
|
||||
"reverse-scrolling-label": "Зворотне прокручування",
|
||||
"show-applications-description": "Відображати значки програм у кожному робочому просторі.",
|
||||
@@ -387,6 +393,7 @@
|
||||
"disconnecting": "Від'єднання...",
|
||||
"download": "Завантажити",
|
||||
"duration": "Тривалість",
|
||||
"dysfunctional": "Дисфункціональний",
|
||||
"edit": "Редагувати",
|
||||
"enabled": "Увімкнено",
|
||||
"events": "Події",
|
||||
@@ -807,6 +814,7 @@
|
||||
"download-title": "Завантажити кольорові схеми",
|
||||
"method-description": {
|
||||
"content": "Схема Material Design з високоточною екстракцією кольорів, яка точно відповідає фактичним кольорам вихідного контенту.",
|
||||
"dysfunctional": "Як Faithful, але вибирає другу за домінантністю колірну родину як основну.",
|
||||
"faithful": "Намагається залишатися близьким до вихідного кольору, створюючи при цьому гармонійну палітру.",
|
||||
"fruit-salad": "Схема Material Design, яка створює грайливу, яскраву палітру з різноманітними відтінками.",
|
||||
"monochrome": "Схема Material Design, що використовує однотонну градацію сірого з мінімальним хроматичним вмістом.",
|
||||
@@ -1302,6 +1310,7 @@
|
||||
"sources-placeholder": "Мій крутий репозиторій",
|
||||
"sources-remove-tooltip": "Видалити джерело плагіна",
|
||||
"title": "Плагіни",
|
||||
"translations-reloaded": "Переклади перезавантажено: {name}",
|
||||
"uninstall-dialog-description": "Ви впевнені, що хочете видалити {plugin}? Це призведе до видалення всіх даних плагіна.",
|
||||
"uninstall-dialog-title": "Видалити плагін",
|
||||
"uninstall-error": "Не вдалося видалити: {error}",
|
||||
@@ -1349,6 +1358,7 @@
|
||||
"system-monitor": {
|
||||
"critical-color-label": "Критичний колір",
|
||||
"custom-highlight-colors-title-label": "Власні кольори підсвічування",
|
||||
"disk-available-label": "Доступно місця на диску",
|
||||
"disk-section-label": "Використання диска",
|
||||
"enable-dgpu-monitoring-description": "Увага: Це розбудить ваш дискретний графічний процесор (NVIDIA/AMD), що може значно вплинути на час роботи акумулятора на ноутбуках з гібридною графікою.",
|
||||
"enable-dgpu-monitoring-label": "Увімкнути моніторинг дискретного GPU",
|
||||
|
||||
@@ -211,6 +211,10 @@
|
||||
"memory-usage-label": "内存使用率",
|
||||
"network-traffic-description": "显示网络上传和下载速度。",
|
||||
"network-traffic-label": "网络流量",
|
||||
"storage-as-percentage-description": "以百分比而非绝对值显示磁盘空间。",
|
||||
"storage-as-percentage-label": "磁盘百分比",
|
||||
"storage-available-description": "显示可用磁盘空间,而非已用空间。",
|
||||
"storage-available-label": "可用磁盘空间",
|
||||
"storage-usage-description": "显示磁盘空间使用情况。",
|
||||
"storage-usage-label": "存储用量",
|
||||
"swap-usage-description": "显示 Swap 使用情况。",
|
||||
@@ -272,6 +276,8 @@
|
||||
"label-mode-label": "标签模式",
|
||||
"occupied-color-description": "设置已占用Workspace的背景颜色。",
|
||||
"occupied-color-label": "已占用工作区颜色",
|
||||
"pill-size-description": "调整工作区指示器的大小 (50%-100%)。",
|
||||
"pill-size-label": "胶囊大小",
|
||||
"reverse-scrolling-description": "滚动时反转工作区切换方向。",
|
||||
"reverse-scrolling-label": "反转滚动",
|
||||
"show-applications-description": "在每个工作区内显示应用程序图标。",
|
||||
@@ -387,6 +393,7 @@
|
||||
"disconnecting": "正在断开连接...",
|
||||
"download": "下载",
|
||||
"duration": "时长",
|
||||
"dysfunctional": "功能失调",
|
||||
"edit": "编辑",
|
||||
"enabled": "已启用",
|
||||
"events": "事件",
|
||||
@@ -807,6 +814,7 @@
|
||||
"download-title": "下载配色方案",
|
||||
"method-description": {
|
||||
"content": "具有高保真色彩提取的 Material Design 方案,可与源内容的实际颜色紧密匹配。",
|
||||
"dysfunctional": "与 Faithful 类似,但选择第二主导的颜色家族作为主要颜色。",
|
||||
"faithful": "尝试在生成和谐调色板的同时,尽可能接近源颜色。",
|
||||
"fruit-salad": "Material Design 方案,产生一个俏皮、充满活力的调色板,具有多样和变化的色调。",
|
||||
"monochrome": "使用单色调灰阶和最少色彩内容的 Material Design 方案。",
|
||||
@@ -1302,6 +1310,7 @@
|
||||
"sources-placeholder": "我的酷仓库",
|
||||
"sources-remove-tooltip": "移除插件源",
|
||||
"title": "插件",
|
||||
"translations-reloaded": "已重新加载翻译:{name}",
|
||||
"uninstall-dialog-description": "你确定要卸载 {plugin} 吗? 这将移除所有插件数据。",
|
||||
"uninstall-dialog-title": "卸载插件",
|
||||
"uninstall-error": "卸载失败:{error}",
|
||||
@@ -1349,6 +1358,7 @@
|
||||
"system-monitor": {
|
||||
"critical-color-label": "严重颜色",
|
||||
"custom-highlight-colors-title-label": "自定义高亮颜色",
|
||||
"disk-available-label": "可用磁盘空间",
|
||||
"disk-section-label": "磁盘使用率",
|
||||
"enable-dgpu-monitoring-description": "警告:这将唤醒您的独立显卡 (NVIDIA/AMD),这可能会显著影响配备混合显卡的笔记本电脑的电池续航时间。",
|
||||
"enable-dgpu-monitoring-label": "启用独立显卡监控",
|
||||
|
||||
@@ -211,6 +211,10 @@
|
||||
"memory-usage-label": "記憶體使用量",
|
||||
"network-traffic-description": "顯示網路上傳及下載的速度",
|
||||
"network-traffic-label": "網路流量",
|
||||
"storage-as-percentage-description": "以百分比而非絕對值顯示磁碟空間。",
|
||||
"storage-as-percentage-label": "磁碟百分比",
|
||||
"storage-available-description": "顯示可用磁碟空間,而非已用空間。",
|
||||
"storage-available-label": "可用磁碟空間",
|
||||
"storage-usage-description": "顯示磁碟空間的使用量",
|
||||
"storage-usage-label": "磁碟使用量",
|
||||
"swap-usage-description": "顯示交換記憶體使用量",
|
||||
@@ -272,6 +276,8 @@
|
||||
"label-mode-label": "標籤樣式",
|
||||
"occupied-color-description": "設定已佔用Workspace的背景顏色。",
|
||||
"occupied-color-label": "已佔用工作區顏色",
|
||||
"pill-size-description": "調整工作區指示器的大小 (50%-100%)。",
|
||||
"pill-size-label": "膠囊大小",
|
||||
"reverse-scrolling-description": "捲動時反轉工作區切換方向。",
|
||||
"reverse-scrolling-label": "反轉捲動",
|
||||
"show-applications-description": "顯示各個工作區的程式圖示",
|
||||
@@ -387,6 +393,7 @@
|
||||
"disconnecting": "正在斷線...",
|
||||
"download": "下載",
|
||||
"duration": "持續時間",
|
||||
"dysfunctional": "功能失調",
|
||||
"edit": "編輯",
|
||||
"enabled": "已啟用",
|
||||
"events": "事件",
|
||||
@@ -807,6 +814,7 @@
|
||||
"download-title": "下載配色",
|
||||
"method-description": {
|
||||
"content": "具有高保真色彩提取的 Material Design 方案,可與來源內容的實際色彩緊密匹配。",
|
||||
"dysfunctional": "與 Faithful 類似,但選擇第二主導的顏色家族作為主要顏色。",
|
||||
"faithful": "嘗試在生成和諧調色盤的同時,盡可能接近來源顏色。",
|
||||
"fruit-salad": "Material Design 配色方案,可產生一個俏皮、充滿活力的調色盤,具有多樣且多變的色調。",
|
||||
"monochrome": "使用單一色調灰階和最少色彩內容的 Material Design 方案。",
|
||||
@@ -1302,6 +1310,7 @@
|
||||
"sources-placeholder": "我的超酷儲存庫",
|
||||
"sources-remove-tooltip": "移除外掛模組來源",
|
||||
"title": "外掛模組 (Plugins)",
|
||||
"translations-reloaded": "已重新載入翻譯:{name}",
|
||||
"uninstall-dialog-description": "你確定想要移除 {plugin}? 這樣會移除所有模組的資料",
|
||||
"uninstall-dialog-title": "移除外掛模組",
|
||||
"uninstall-error": "移除失敗: {error}",
|
||||
@@ -1349,6 +1358,7 @@
|
||||
"system-monitor": {
|
||||
"critical-color-label": "危急顏色",
|
||||
"custom-highlight-colors-title-label": "自訂突出色",
|
||||
"disk-available-label": "可用磁碟空間",
|
||||
"disk-section-label": "磁碟使用量",
|
||||
"enable-dgpu-monitoring-description": "注意: 這個選項會喚醒你的 GPU (NVIDIA/AMD), 在混合顯卡的筆電上可能會嚴重影響電池續航力",
|
||||
"enable-dgpu-monitoring-label": "啟用監視獨立 GPU",
|
||||
|
||||
@@ -270,6 +270,8 @@
|
||||
"swapCriticalThreshold": 90,
|
||||
"diskWarningThreshold": 80,
|
||||
"diskCriticalThreshold": 90,
|
||||
"diskAvailWarningThreshold": 20,
|
||||
"diskAvailCriticalThreshold": 10,
|
||||
"cpuPollingInterval": 1000,
|
||||
"gpuPollingInterval": 3000,
|
||||
"enableDgpuMonitoring": false,
|
||||
|
||||
@@ -138,7 +138,8 @@
|
||||
"showSwapUsage": false,
|
||||
"showNetworkStats": false,
|
||||
"showDiskUsage": false,
|
||||
"showDiskAsFree": false,
|
||||
"showDiskUsageAsPercent": false,
|
||||
"showDiskAvailable": false,
|
||||
"diskPath": "/"
|
||||
},
|
||||
"Taskbar": {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
function migrate(adapter, logger, rawJson) {
|
||||
logger.i("Migration47", "Removing network_stats.json cache and updating polling intervals");
|
||||
|
||||
// Remove the network_stats.json cache file (no longer used - autoscaling from history now)
|
||||
const shellName = "noctalia";
|
||||
const cacheDir = Quickshell.env("NOCTALIA_CACHE_DIR") || (Quickshell.env("XDG_CACHE_HOME") || Quickshell.env("HOME") + "/.cache") + "/" + shellName + "/";
|
||||
const networkStatsFile = cacheDir + "network_stats.json";
|
||||
Quickshell.execDetached(["rm", "-f", networkStatsFile]);
|
||||
|
||||
// Update polling intervals to 1000ms for smoother graphs (only if currently slower)
|
||||
if (adapter.systemMonitor.cpuPollingInterval > 1000)
|
||||
adapter.systemMonitor.cpuPollingInterval = 1000;
|
||||
if (adapter.systemMonitor.memPollingInterval > 1000)
|
||||
adapter.systemMonitor.memPollingInterval = 1000;
|
||||
if (adapter.systemMonitor.networkPollingInterval > 1000)
|
||||
adapter.systemMonitor.networkPollingInterval = 1000;
|
||||
|
||||
logger.d("Migration47", "Removed network_stats.json and adjusted polling intervals");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,8 @@ QtObject {
|
||||
43: migration43Component,
|
||||
44: migration44Component,
|
||||
45: migration45Component,
|
||||
46: migration46Component
|
||||
46: migration46Component,
|
||||
47: migration47Component
|
||||
})
|
||||
|
||||
// Migration components
|
||||
@@ -38,4 +39,5 @@ QtObject {
|
||||
property Component migration44Component: Migration44 {}
|
||||
property Component migration45Component: Migration45 {}
|
||||
property Component migration46Component: Migration46 {}
|
||||
property Component migration47Component: Migration47 {}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ Singleton {
|
||||
- Default cache directory: ~/.cache/noctalia
|
||||
*/
|
||||
readonly property alias data: adapter // Used to access via Settings.data.xxx.yyy
|
||||
readonly property int settingsVersion: 46
|
||||
readonly property int settingsVersion: 47
|
||||
readonly property bool isDebug: Quickshell.env("NOCTALIA_DEBUG") === "1"
|
||||
readonly property string shellName: "noctalia"
|
||||
readonly property string configDir: Quickshell.env("NOCTALIA_CONFIG_DIR") || (Quickshell.env("XDG_CONFIG_HOME") || Quickshell.env("HOME") + "/.config") + "/" + shellName + "/"
|
||||
@@ -482,8 +482,9 @@ Singleton {
|
||||
property int swapCriticalThreshold: 90
|
||||
property int diskWarningThreshold: 80
|
||||
property int diskCriticalThreshold: 90
|
||||
property int cpuPollingInterval: 3000
|
||||
property int tempPollingInterval: 3000
|
||||
property int diskAvailWarningThreshold: 20
|
||||
property int diskAvailCriticalThreshold: 10
|
||||
property int cpuPollingInterval: 1000
|
||||
property int gpuPollingInterval: 3000
|
||||
property bool enableDgpuMonitoring: false // Opt-in: reading dGPU sysfs/nvidia-smi wakes it from D3cold, draining battery
|
||||
property int memPollingInterval: 1000
|
||||
|
||||
@@ -293,6 +293,85 @@ PopupWindow {
|
||||
anchors.rightMargin: Style.marginM
|
||||
spacing: Style.marginS
|
||||
|
||||
// Indicator Container
|
||||
Item {
|
||||
visible: (modelData?.buttonType ?? QsMenuButtonType.None) !== QsMenuButtonType.None
|
||||
|
||||
implicitWidth: Math.round(Style.baseWidgetSize * 0.5)
|
||||
implicitHeight: Math.round(Style.baseWidgetSize * 0.5)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
// Helper properties
|
||||
readonly property int type: modelData?.buttonType ?? QsMenuButtonType.None
|
||||
readonly property bool isRadio: type === QsMenuButtonType.RadioButton
|
||||
readonly property bool isChecked: modelData?.checkState === Qt.Checked || (modelData?.checked ?? false)
|
||||
|
||||
// Color Logic
|
||||
readonly property color activeColor: mouseArea.containsMouse ? Color.mOnHover : Color.mPrimary
|
||||
readonly property color checkMarkColor: mouseArea.containsMouse ? Color.mHover : Color.mOnPrimary
|
||||
readonly property color borderColor: isChecked ? activeColor : (mouseArea.containsMouse ? Color.mOnHover : Color.mOnSurface)
|
||||
|
||||
// Checkbox Visuals
|
||||
Rectangle {
|
||||
visible: !parent.isRadio
|
||||
anchors.centerIn: parent
|
||||
width: Math.round(Style.baseWidgetSize * 0.5)
|
||||
height: Math.round(Style.baseWidgetSize * 0.5)
|
||||
radius: Style.iRadiusXS
|
||||
color: "transparent" // Transparent to match RadioButton style
|
||||
border.color: parent.borderColor
|
||||
border.width: Style.borderM
|
||||
|
||||
Behavior on border.color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
|
||||
NIcon {
|
||||
visible: parent.parent.isChecked
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: -1
|
||||
icon: "check"
|
||||
color: parent.parent.activeColor
|
||||
pointSize: Math.max(Style.fontSizeXXS, parent.width * 0.6)
|
||||
}
|
||||
}
|
||||
|
||||
// RadioButton Visuals
|
||||
Rectangle {
|
||||
visible: parent.isRadio
|
||||
anchors.centerIn: parent
|
||||
width: Style.toOdd(Style.baseWidgetSize * 0.5)
|
||||
height: Style.toOdd(Style.baseWidgetSize * 0.5)
|
||||
radius: width / 2
|
||||
color: "transparent"
|
||||
border.color: parent.borderColor
|
||||
border.width: Style.borderM // Slightly thicker for radio look
|
||||
|
||||
Behavior on border.color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: parent.parent.isChecked
|
||||
anchors.centerIn: parent
|
||||
width: Style.toOdd(parent.width * 0.5)
|
||||
height: Style.toOdd(parent.height * 0.5)
|
||||
radius: width / 2
|
||||
color: parent.parent.activeColor
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
id: text
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -117,7 +117,7 @@ Item {
|
||||
if (action === "toggle-mute") {
|
||||
AudioService.setInputMuted(!AudioService.inputMuted);
|
||||
} else if (action === "custom-command") {
|
||||
Quickshell.execDetached(["sh", "-lc", middleClickCommand]);
|
||||
Quickshell.execDetached(["sh", "-c", middleClickCommand]);
|
||||
} else if (action === "widget-settings") {
|
||||
BarService.openWidgetSettings(screen, section, sectionWidgetIndex, widgetId, widgetSettings);
|
||||
}
|
||||
@@ -167,7 +167,7 @@ Item {
|
||||
PanelService.showContextMenu(contextMenu, pill, screen);
|
||||
}
|
||||
onMiddleClicked: {
|
||||
Quickshell.execDetached(["sh", "-lc", middleClickCommand]);
|
||||
Quickshell.execDetached(["sh", "-c", middleClickCommand]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@ Item {
|
||||
readonly property bool showSwapUsage: (widgetSettings.showSwapUsage !== undefined) ? widgetSettings.showSwapUsage : widgetMetadata.showSwapUsage
|
||||
readonly property bool showNetworkStats: (widgetSettings.showNetworkStats !== undefined) ? widgetSettings.showNetworkStats : widgetMetadata.showNetworkStats
|
||||
readonly property bool showDiskUsage: (widgetSettings.showDiskUsage !== undefined) ? widgetSettings.showDiskUsage : widgetMetadata.showDiskUsage
|
||||
readonly property bool showDiskAsFree: (widgetSettings.showDiskAsFree !== undefined) ? widgetSettings.showDiskAsFree : widgetMetadata.showDiskAsFree
|
||||
readonly property bool showDiskUsageAsPercent: (widgetSettings.showDiskUsageAsPercent !== undefined) ? widgetSettings.showDiskUsageAsPercent : widgetMetadata.showDiskUsageAsPercent
|
||||
readonly property bool showDiskAvailable: (widgetSettings.showDiskAvailable !== undefined) ? widgetSettings.showDiskAvailable : widgetMetadata.showDiskAvailable
|
||||
readonly property bool showLoadAverage: (widgetSettings.showLoadAverage !== undefined) ? widgetSettings.showLoadAverage : widgetMetadata.showLoadAverage
|
||||
readonly property string diskPath: (widgetSettings.diskPath !== undefined) ? widgetSettings.diskPath : widgetMetadata.diskPath
|
||||
readonly property string fontFamily: useMonospaceFont ? Settings.data.ui.fontFixed : Settings.data.ui.fontDefault
|
||||
@@ -94,11 +95,11 @@ Item {
|
||||
}
|
||||
|
||||
// Memory
|
||||
rows.push([I18n.tr("common.memory"), `${Math.round(SystemStatService.memPercent)}% (${SystemStatService.formatMemoryGb(SystemStatService.memGb).replace(/[^0-9.]/g, "") + " GB"})`]);
|
||||
rows.push([I18n.tr("common.memory"), `${Math.round(SystemStatService.memPercent)}% (${SystemStatService.formatGigabytes(SystemStatService.memGb).replace(/[^0-9.]/g, "") + " GB"})`]);
|
||||
|
||||
// Swap (if available)
|
||||
if (SystemStatService.swapTotalGb > 0) {
|
||||
rows.push([I18n.tr("bar.system-monitor.swap-usage-label"), `${Math.round(SystemStatService.swapPercent)}% (${SystemStatService.formatMemoryGb(SystemStatService.swapGb).replace(/[^0-9.]/g, "") + " GB"})`]);
|
||||
rows.push([I18n.tr("bar.system-monitor.swap-usage-label"), `${Math.round(SystemStatService.swapPercent)}% (${SystemStatService.formatGigabytes(SystemStatService.swapGb).replace(/[^0-9.]/g, "") + " GB"})`]);
|
||||
}
|
||||
|
||||
// Network
|
||||
@@ -110,11 +111,9 @@ Item {
|
||||
if (diskPercent !== undefined) {
|
||||
const usedGb = SystemStatService.diskUsedGb[diskPath] || 0;
|
||||
const sizeGb = SystemStatService.diskSizeGb[diskPath] || 0;
|
||||
const availGb = SystemStatService.diskAvailGb[diskPath] || 0;
|
||||
rows.push([I18n.tr("system-monitor.disk"), `${usedGb.toFixed(1)}GB/${sizeGb.toFixed(1)}GB (${diskPercent}%)`]);
|
||||
|
||||
// TODO i18n
|
||||
rows.push(["Available", `${availGb.toFixed(1)}G`]);
|
||||
const availGb = SystemStatService.diskAvailableGb[diskPath] || 0;
|
||||
rows.push([I18n.tr("system-monitor.disk"), `${diskPercent}% (${usedGb.toFixed(1)} / ${sizeGb.toFixed(1)} GB)`]);
|
||||
rows.push([I18n.tr("common.available"), `${availGb.toFixed(1)} GB`]);
|
||||
}
|
||||
|
||||
return rows;
|
||||
@@ -597,7 +596,7 @@ Item {
|
||||
// Text mode
|
||||
NText {
|
||||
visible: !compactMode
|
||||
text: showMemoryAsPercent ? `${Math.round(SystemStatService.memPercent)}%` : SystemStatService.formatMemoryGb(SystemStatService.memGb)
|
||||
text: showMemoryAsPercent ? `${Math.round(SystemStatService.memPercent)}%` : SystemStatService.formatGigabytes(SystemStatService.memGb)
|
||||
family: fontFamily
|
||||
pointSize: barFontSize
|
||||
applyUiScale: false
|
||||
@@ -863,14 +862,10 @@ Item {
|
||||
// Text mode
|
||||
NText {
|
||||
visible: !compactMode
|
||||
text: {
|
||||
if (showDiskAsFree && !isVertical) {
|
||||
let avail = SystemStatService.diskAvailGb[diskPath] || 0;
|
||||
return avail.toFixed(1) + "G";
|
||||
} else {
|
||||
return SystemStatService.diskPercents[diskPath] ? `${SystemStatService.diskPercents[diskPath]}%` : "n/a";
|
||||
}
|
||||
}
|
||||
text: SystemStatService.formatDiskDisplay(diskPath, {
|
||||
percent: showDiskUsageAsPercent,
|
||||
available: showDiskAvailable
|
||||
})
|
||||
family: fontFamily
|
||||
pointSize: barFontSize
|
||||
applyUiScale: false
|
||||
@@ -892,8 +887,8 @@ Item {
|
||||
Layout.column: 1
|
||||
|
||||
onLoaded: {
|
||||
item.ratio = Qt.binding(() => (SystemStatService.diskPercents[diskPath] ?? 0) / 100);
|
||||
item.statColor = Qt.binding(() => SystemStatService.getDiskColor(diskPath));
|
||||
item.ratio = Qt.binding(() => (showDiskAvailable ? SystemStatService.diskAvailPercents[diskPath] : SystemStatService.diskPercents[diskPath] ?? 0) / 100);
|
||||
item.statColor = Qt.binding(() => SystemStatService.getDiskColor(diskPath, showDiskAvailable));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ Item {
|
||||
if (action === "toggle-mute") {
|
||||
AudioService.setOutputMuted(!AudioService.muted);
|
||||
} else if (action === "custom-command") {
|
||||
Quickshell.execDetached(["sh", "-lc", middleClickCommand]);
|
||||
Quickshell.execDetached(["sh", "-c", middleClickCommand]);
|
||||
} else if (action === "widget-settings") {
|
||||
BarService.openWidgetSettings(screen, section, sectionWidgetIndex, widgetId, widgetSettings);
|
||||
}
|
||||
@@ -149,7 +149,7 @@ Item {
|
||||
PanelService.showContextMenu(contextMenu, pill, screen);
|
||||
}
|
||||
onMiddleClicked: {
|
||||
Quickshell.execDetached(["sh", "-lc", middleClickCommand]);
|
||||
Quickshell.execDetached(["sh", "-c", middleClickCommand]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ Item {
|
||||
readonly property real barHeight: Style.getBarHeightForScreen(screenName)
|
||||
readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screenName)
|
||||
readonly property real barFontSize: Style.getBarFontSizeForScreen(screenName)
|
||||
readonly property real baseDimensionRatio: 0.65
|
||||
|
||||
readonly property string labelMode: (widgetSettings.labelMode !== undefined) ? widgetSettings.labelMode : widgetMetadata.labelMode
|
||||
readonly property bool hasLabel: (labelMode !== "none")
|
||||
@@ -49,6 +48,12 @@ Item {
|
||||
readonly property bool followFocusedScreen: (widgetSettings.followFocusedScreen !== undefined) ? widgetSettings.followFocusedScreen : widgetMetadata.followFocusedScreen
|
||||
readonly property int characterCount: isVertical ? 2 : ((widgetSettings.characterCount !== undefined) ? widgetSettings.characterCount : widgetMetadata.characterCount)
|
||||
|
||||
// Pill size setting (0.5-1.0 range)
|
||||
readonly property real pillSize: (widgetSettings.pillSize !== undefined) ? widgetSettings.pillSize : widgetMetadata.pillSize
|
||||
|
||||
// When no label the pills are smaller
|
||||
readonly property real baseDimensionRatio: pillSize
|
||||
|
||||
// Grouped mode (show applications) settings
|
||||
readonly property bool showApplications: (widgetSettings.showApplications !== undefined) ? widgetSettings.showApplications : widgetMetadata.showApplications
|
||||
readonly property bool showLabelsOnlyWhenOccupied: (widgetSettings.showLabelsOnlyWhenOccupied !== undefined) ? widgetSettings.showLabelsOnlyWhenOccupied : widgetMetadata.showLabelsOnlyWhenOccupied
|
||||
|
||||
+25
-13
@@ -649,8 +649,8 @@ Loader {
|
||||
Flickable {
|
||||
id: dock
|
||||
// Use parent dimensions more directly to avoid clipping
|
||||
width: isVertical ? parent.width - Style.marginS * 2 : Math.min(dockLayout.implicitWidth, parent.width - Style.marginXL)
|
||||
height: !isVertical ? parent.height - Style.marginS * 2 : Math.min(dockLayout.implicitHeight, parent.height - Style.marginXL)
|
||||
width: isVertical ? parent.width : Math.min(dockLayout.implicitWidth, parent.width - Style.marginXL)
|
||||
height: !isVertical ? parent.height : Math.min(dockLayout.implicitHeight, parent.height - Style.marginXL)
|
||||
contentWidth: dockLayout.implicitWidth
|
||||
contentHeight: dockLayout.implicitHeight
|
||||
anchors.centerIn: parent
|
||||
@@ -662,10 +662,8 @@ Loader {
|
||||
interactive: isVertical ? contentHeight > height : contentWidth > width
|
||||
|
||||
// Centering margins
|
||||
leftMargin: contentWidth < width ? (width - contentWidth) / 2 : 0
|
||||
rightMargin: leftMargin
|
||||
topMargin: contentHeight < height ? (height - contentHeight) / 2 : 0
|
||||
bottomMargin: topMargin
|
||||
contentX: isVertical && contentWidth < width ? (contentWidth - width) / 2 : 0
|
||||
contentY: !isVertical && contentHeight < height ? (contentHeight - height) / 2 : 0
|
||||
|
||||
WheelHandler {
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
@@ -714,8 +712,9 @@ Loader {
|
||||
|
||||
delegate: Item {
|
||||
id: appButton
|
||||
Layout.preferredWidth: iconSize
|
||||
Layout.preferredHeight: iconSize
|
||||
readonly property real indicatorMargin: Math.max(3, Math.round(iconSize * 0.18))
|
||||
Layout.preferredWidth: isVertical ? iconSize + indicatorMargin * 2 : iconSize
|
||||
Layout.preferredHeight: isVertical ? iconSize : iconSize + indicatorMargin * 2
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
|
||||
property bool isActive: modelData.toplevel && ToplevelManager.activeToplevel && ToplevelManager.activeToplevel === modelData.toplevel
|
||||
@@ -1072,15 +1071,28 @@ Loader {
|
||||
}
|
||||
}
|
||||
|
||||
// Active indicator - always below the icon
|
||||
// Active indicator - positioned at the edge of the delegate area
|
||||
Rectangle {
|
||||
visible: Settings.data.dock.inactiveIndicators ? isRunning : isActive
|
||||
width: iconSize * 0.2
|
||||
height: iconSize * 0.1
|
||||
width: isVertical ? indicatorMargin * 0.6 : iconSize * 0.2
|
||||
height: isVertical ? iconSize * 0.2 : indicatorMargin * 0.6
|
||||
color: Color.mPrimary
|
||||
radius: Style.radiusXS
|
||||
anchors.top: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
// Anchor to the edge facing the screen center
|
||||
anchors.bottom: !isVertical && dockPosition === "bottom" ? parent.bottom : undefined
|
||||
anchors.top: !isVertical && dockPosition === "top" ? parent.top : undefined
|
||||
anchors.left: isVertical && dockPosition === "left" ? parent.left : undefined
|
||||
anchors.right: isVertical && dockPosition === "right" ? parent.right : undefined
|
||||
|
||||
anchors.horizontalCenter: isVertical ? undefined : parent.horizontalCenter
|
||||
anchors.verticalCenter: isVertical ? parent.verticalCenter : undefined
|
||||
|
||||
// Offset slightly from the edge
|
||||
anchors.bottomMargin: !isVertical && dockPosition === "bottom" ? 2 : 0
|
||||
anchors.topMargin: !isVertical && dockPosition === "top" ? 2 : 0
|
||||
anchors.leftMargin: isVertical && dockPosition === "left" ? 2 : 0
|
||||
anchors.rightMargin: isVertical && dockPosition === "right" ? 2 : 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,12 +102,15 @@ Variants {
|
||||
}
|
||||
|
||||
// BarExclusionZone - created after MainScreen has fully loaded
|
||||
// Disabled when bar is hidden or not configured for this screen
|
||||
// Note: Exclusion zone should NOT be affected by hideOnOverview setting.
|
||||
// When bar is hidden during overview, the exclusion zone should remain to prevent
|
||||
// windows from moving into the bar area. Auto-hide is handled by the component
|
||||
// itself via ExclusionMode.Ignore/Auto.
|
||||
Repeater {
|
||||
model: Settings.data.bar.barType === "framed" ? ["top", "bottom", "left", "right"] : [Settings.getBarPositionForScreen(windowItem.modelData?.name)]
|
||||
delegate: Loader {
|
||||
active: {
|
||||
if (!windowItem.windowLoaded || !windowItem.shouldBeActive || !BarService.effectivelyVisible)
|
||||
if (!windowItem.windowLoaded || !windowItem.shouldBeActive)
|
||||
return false;
|
||||
|
||||
// Check if bar is configured for this screen
|
||||
|
||||
@@ -15,15 +15,6 @@ SmartPanel {
|
||||
preferredWidth: Math.round(440 * Style.uiScaleRatio)
|
||||
preferredHeight: Math.round(420 * Style.uiScaleRatio)
|
||||
|
||||
onOpened: {
|
||||
// Refresh DDC brightness from monitors (one-time on panel open)
|
||||
BrightnessService.monitors.forEach(m => {
|
||||
if (m.isDdc) {
|
||||
m.refreshBrightnessFromSystem();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
panelContent: Item {
|
||||
id: panelContent
|
||||
property real contentPreferredHeight: mainColumn.implicitHeight + Style.marginL * 2
|
||||
|
||||
@@ -1585,6 +1585,24 @@ SmartPanel {
|
||||
font.weight: Style.fontWeightBold
|
||||
color: modelData.displayString ? Color.mOnSurface : Color.mOnPrimary
|
||||
}
|
||||
|
||||
// Badge icon overlay (generic indicator for any provider)
|
||||
Rectangle {
|
||||
visible: !!modelData.badgeIcon
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.margins: 2
|
||||
width: height
|
||||
height: Style.fontSizeM + Style.marginXS
|
||||
color: Color.mSurfaceVariant
|
||||
radius: Style.radiusXXS
|
||||
NIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: modelData.badgeIcon || ""
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Text content (hidden when hideLabel is true)
|
||||
|
||||
@@ -41,7 +41,7 @@ Item {
|
||||
"onActivate": function () {
|
||||
launcher.closeImmediately();
|
||||
Qt.callLater(() => {
|
||||
Quickshell.execDetached(["sh", "-lc", expression]);
|
||||
Quickshell.execDetached(["sh", "-c", expression]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ Item {
|
||||
// If custom command is defined, execute it
|
||||
if (command && command.trim() !== "") {
|
||||
Logger.i("SessionProvider", "Executing custom command for action:", action, "Command:", command);
|
||||
Quickshell.execDetached(["sh", "-lc", command]);
|
||||
Quickshell.execDetached(["sh", "-c", command]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -222,7 +222,7 @@ SmartPanel {
|
||||
// If custom command is defined, execute it
|
||||
if (option && option.command && option.command.trim() !== "") {
|
||||
Logger.i("SessionMenu", "Executing custom command for action:", action, "Command:", option.command);
|
||||
Quickshell.execDetached(["sh", "-lc", option.command]);
|
||||
Quickshell.execDetached(["sh", "-c", option.command]);
|
||||
cancelTimer();
|
||||
root.close();
|
||||
return;
|
||||
|
||||
@@ -14,6 +14,11 @@ NBox {
|
||||
|
||||
required property var screen
|
||||
readonly property string screenName: screen?.name || ""
|
||||
// Determine if the bar on a per screen basis is vertical
|
||||
readonly property bool barIsVertical: {
|
||||
var pos = Settings.getBarPositionForScreen(screenName);
|
||||
return pos === "left" || pos === "right";
|
||||
}
|
||||
|
||||
color: Color.mSurfaceVariant
|
||||
Layout.fillWidth: true
|
||||
@@ -153,7 +158,7 @@ NBox {
|
||||
|
||||
// Left Section
|
||||
NSectionEditor {
|
||||
sectionName: I18n.tr("positions.left")
|
||||
sectionName: root.barIsVertical ? I18n.tr("positions.top") : I18n.tr("positions.left")
|
||||
sectionId: "left"
|
||||
screen: root.screen
|
||||
settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Panels/Settings/Bar/BarWidgetSettingsDialog.qml")
|
||||
@@ -187,7 +192,7 @@ NBox {
|
||||
|
||||
// Right Section
|
||||
NSectionEditor {
|
||||
sectionName: I18n.tr("positions.right")
|
||||
sectionName: root.barIsVertical ? I18n.tr("positions.bottom") : I18n.tr("positions.right")
|
||||
sectionId: "right"
|
||||
screen: root.screen
|
||||
settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Panels/Settings/Bar/BarWidgetSettingsDialog.qml")
|
||||
|
||||
@@ -29,7 +29,8 @@ ColumnLayout {
|
||||
property bool valueShowSwapUsage: widgetData.showSwapUsage !== undefined ? widgetData.showSwapUsage : widgetMetadata.showSwapUsage
|
||||
property bool valueShowNetworkStats: widgetData.showNetworkStats !== undefined ? widgetData.showNetworkStats : widgetMetadata.showNetworkStats
|
||||
property bool valueShowDiskUsage: widgetData.showDiskUsage !== undefined ? widgetData.showDiskUsage : widgetMetadata.showDiskUsage
|
||||
property bool valueShowDiskAsFree: widgetData.showDiskAsFree !== undefined ? widgetData.showDiskAsFree : widgetMetadata.showDiskAsFree
|
||||
property bool valueShowDiskUsageAsPercent: widgetData.showDiskUsageAsPercent !== undefined ? widgetData.showDiskUsageAsPercent : widgetMetadata.showDiskUsageAsPercent
|
||||
property bool valueShowDiskAvailable: widgetData.showDiskAvailable !== undefined ? widgetData.showDiskAvailable : widgetMetadata.showDiskAvailable
|
||||
property string valueDiskPath: widgetData.diskPath !== undefined ? widgetData.diskPath : widgetMetadata.diskPath
|
||||
|
||||
function saveSettings() {
|
||||
@@ -47,7 +48,8 @@ ColumnLayout {
|
||||
settings.showSwapUsage = valueShowSwapUsage;
|
||||
settings.showNetworkStats = valueShowNetworkStats;
|
||||
settings.showDiskUsage = valueShowDiskUsage;
|
||||
settings.showDiskAsFree = valueShowDiskAsFree;
|
||||
settings.showDiskUsageAsPercent = valueShowDiskUsageAsPercent;
|
||||
settings.showDiskAvailable = valueShowDiskAvailable;
|
||||
settings.diskPath = valueDiskPath;
|
||||
|
||||
return settings;
|
||||
@@ -211,16 +213,27 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
NToggle {
|
||||
id: showDiskAsFree
|
||||
id: showDiskUsageAsPercent
|
||||
Layout.fillWidth: true
|
||||
label: "Show Free Space" // TODO: use I18n.tr
|
||||
description: "Display available space (GB) instead of percentage" // TODO: use I18n.tr
|
||||
checked: valueShowDiskAsFree
|
||||
label: I18n.tr("bar.system-monitor.storage-as-percentage-label")
|
||||
description: I18n.tr("bar.system-monitor.storage-as-percentage-description")
|
||||
checked: valueShowDiskUsageAsPercent
|
||||
onToggled: checked => {
|
||||
valueShowDiskAsFree = checked;
|
||||
valueShowDiskUsageAsPercent = checked;
|
||||
settingsChanged(saveSettings());
|
||||
}
|
||||
}
|
||||
|
||||
NToggle {
|
||||
id: showDiskAvailable
|
||||
Layout.fillWidth: true
|
||||
label: I18n.tr("bar.system-monitor.storage-available-label")
|
||||
description: I18n.tr("bar.system-monitor.storage-available-description")
|
||||
checked: valueShowDiskAvailable
|
||||
onToggled: checked => {
|
||||
valueShowDiskAvailable = checked;
|
||||
settingsChanged(saveSettings());
|
||||
}
|
||||
visible: valueShowDiskUsage
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
|
||||
@@ -32,6 +32,7 @@ ColumnLayout {
|
||||
property string valueOccupiedColor: widgetData.occupiedColor !== undefined ? widgetData.occupiedColor : widgetMetadata.occupiedColor
|
||||
property string valueEmptyColor: widgetData.emptyColor !== undefined ? widgetData.emptyColor : widgetMetadata.emptyColor
|
||||
property bool valueShowBadge: widgetData.showBadge !== undefined ? widgetData.showBadge : widgetMetadata.showBadge
|
||||
property real valuePillSize: widgetData.pillSize !== undefined ? widgetData.pillSize : widgetMetadata.pillSize
|
||||
|
||||
function saveSettings() {
|
||||
var settings = Object.assign({}, widgetData || {});
|
||||
@@ -51,6 +52,7 @@ ColumnLayout {
|
||||
settings.occupiedColor = valueOccupiedColor;
|
||||
settings.emptyColor = valueEmptyColor;
|
||||
settings.showBadge = valueShowBadge;
|
||||
settings.pillSize = valuePillSize;
|
||||
return settings;
|
||||
}
|
||||
|
||||
@@ -97,6 +99,21 @@ ColumnLayout {
|
||||
visible: valueLabelMode === "name"
|
||||
}
|
||||
|
||||
NValueSlider {
|
||||
label: I18n.tr("bar.workspace.pill-size-label")
|
||||
description: I18n.tr("bar.workspace.pill-size-description")
|
||||
from: 0.4
|
||||
to: 1.0
|
||||
stepSize: 0.01
|
||||
value: valuePillSize
|
||||
onMoved: value => {
|
||||
valuePillSize = value;
|
||||
settingsChanged(saveSettings());
|
||||
}
|
||||
text: Math.round(valuePillSize * 100) + "%"
|
||||
visible: !valueShowApplications
|
||||
}
|
||||
|
||||
NToggle {
|
||||
label: I18n.tr("bar.workspace.hide-unoccupied-label")
|
||||
description: I18n.tr("bar.workspace.hide-unoccupied-description")
|
||||
|
||||
@@ -140,7 +140,7 @@ ColumnLayout {
|
||||
info += "GPU: " + gpu.result.map(g => g.name || "Unknown").join(", ") + "\n";
|
||||
}
|
||||
if (mem?.result) {
|
||||
info += "Memory: " + SystemStatService.formatMemoryGb(mem.result.total / root.giga) + "\n";
|
||||
info += "Memory: " + SystemStatService.formatGigabytes(mem.result.total / root.giga) + "\n";
|
||||
}
|
||||
if (wm?.result) {
|
||||
info += "WM: " + (wm.result.prettyName || wm.result.processName || "N/A") + "\n";
|
||||
@@ -476,17 +476,22 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
|
||||
// Action buttons row
|
||||
RowLayout {
|
||||
GridLayout {
|
||||
id: actionsGrid
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: Style.marginM
|
||||
Layout.bottomMargin: Style.marginM
|
||||
spacing: Style.marginM
|
||||
rowSpacing: Style.marginM
|
||||
columnSpacing: Style.marginM
|
||||
|
||||
columns: (changelogBtn.implicitWidth + copyBtn.implicitWidth + supportBtn.implicitWidth + 2 * columnSpacing) < root.width ? 3 : 1
|
||||
|
||||
NButton {
|
||||
id: changelogBtn
|
||||
icon: "sparkles"
|
||||
text: I18n.tr("panels.about.changelog")
|
||||
outlined: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
onClicked: {
|
||||
var screen = PanelService.openedPanel?.screen || Quickshell.screens[0];
|
||||
UpdateService.viewChangelog(screen);
|
||||
@@ -494,16 +499,20 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
NButton {
|
||||
id: copyBtn
|
||||
icon: "copy"
|
||||
text: I18n.tr("panels.about.copy-info")
|
||||
outlined: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
onClicked: root.copyInfoToClipboard()
|
||||
}
|
||||
|
||||
NButton {
|
||||
id: supportBtn
|
||||
icon: "heart"
|
||||
text: I18n.tr("panels.about.support")
|
||||
outlined: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
onClicked: {
|
||||
Quickshell.execDetached(["xdg-open", "https://buymeacoffee.com/noctalia"]);
|
||||
ToastService.showNotice(I18n.tr("panels.about.support"), I18n.tr("toast.kofi-opened"));
|
||||
@@ -688,8 +697,8 @@ ColumnLayout {
|
||||
const mem = root.getModule("Memory");
|
||||
if (!mem?.result)
|
||||
return "N/A";
|
||||
const used = SystemStatService.formatMemoryGb(mem.result.used / root.giga);
|
||||
const total = SystemStatService.formatMemoryGb(mem.result.total / root.giga);
|
||||
const used = SystemStatService.formatGigabytes(mem.result.used / root.giga);
|
||||
const total = SystemStatService.formatGigabytes(mem.result.total / root.giga);
|
||||
return used + " / " + total;
|
||||
}
|
||||
color: Color.mOnSurface
|
||||
@@ -712,8 +721,8 @@ ColumnLayout {
|
||||
const rootDisk = disk.result.find(d => d.mountpoint === "/");
|
||||
if (!rootDisk?.bytes)
|
||||
return "N/A";
|
||||
const used = SystemStatService.formatMemoryGb(rootDisk.bytes.used / root.giga);
|
||||
const total = SystemStatService.formatMemoryGb(rootDisk.bytes.total / root.giga);
|
||||
const used = SystemStatService.formatGigabytes(rootDisk.bytes.used / root.giga);
|
||||
const total = SystemStatService.formatGigabytes(rootDisk.bytes.total / root.giga);
|
||||
return used + " / " + total + " (" + rootDisk.filesystem + ")";
|
||||
}
|
||||
color: Color.mOnSurface
|
||||
|
||||
@@ -19,6 +19,8 @@ ColumnLayout {
|
||||
property var moveWidgetBetweenSections
|
||||
|
||||
signal openPluginSettings(var manifest)
|
||||
// determine if the bar is vertical
|
||||
readonly property bool barIsVertical: Settings.data.bar.position === "left" || Settings.data.bar.position === "right"
|
||||
|
||||
function getSectionIcons() {
|
||||
return {
|
||||
@@ -36,7 +38,7 @@ ColumnLayout {
|
||||
|
||||
// Left Section
|
||||
NSectionEditor {
|
||||
sectionName: I18n.tr("positions.left")
|
||||
sectionName: root.barIsVertical ? I18n.tr("positions.top") : I18n.tr("positions.left")
|
||||
sectionId: "left"
|
||||
settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Panels/Settings/Bar/BarWidgetSettingsDialog.qml")
|
||||
widgetRegistry: BarWidgetRegistry
|
||||
@@ -70,7 +72,7 @@ ColumnLayout {
|
||||
|
||||
// Right Section
|
||||
NSectionEditor {
|
||||
sectionName: I18n.tr("positions.right")
|
||||
sectionName: root.barIsVertical ? I18n.tr("positions.bottom") : I18n.tr("positions.right")
|
||||
sectionId: "right"
|
||||
settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Panels/Settings/Bar/BarWidgetSettingsDialog.qml")
|
||||
widgetRegistry: BarWidgetRegistry
|
||||
|
||||
@@ -56,11 +56,19 @@ ColumnLayout {
|
||||
if (app.id === "discord") {
|
||||
include = TemplateProcessor.isDiscordClientEnabled(client.name);
|
||||
} else if (app.id === "code") {
|
||||
include = TemplateProcessor.isCodeClientEnabled(client.name);
|
||||
// For code clients, resolve all theme paths dynamically (version-independent)
|
||||
if (TemplateProcessor.isCodeClientEnabled(client.name)) {
|
||||
var resolvedPaths = TemplateRegistry.resolvedCodeClientPaths(client.name);
|
||||
for (var p = 0; p < resolvedPaths.length; p++) {
|
||||
validClients.push(resolvedPaths[p]);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (include) {
|
||||
validClients.push(client.path);
|
||||
if (client.path)
|
||||
validClients.push(client.path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -243,5 +243,38 @@ ColumnLayout {
|
||||
suffix: "%"
|
||||
onValueChanged: Settings.data.systemMonitor.diskCriticalThreshold = value
|
||||
}
|
||||
|
||||
// Disk Available
|
||||
NText {
|
||||
text: I18n.tr("panels.system-monitor.disk-available-label")
|
||||
pointSize: Style.fontSizeM
|
||||
}
|
||||
|
||||
NSpinBox {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
from: 0
|
||||
to: 100
|
||||
stepSize: 5
|
||||
value: Settings.data.systemMonitor.diskAvailWarningThreshold
|
||||
defaultValue: Settings.getDefaultValue("systemMonitor.diskAvailWarningThreshold")
|
||||
suffix: "%"
|
||||
onValueChanged: {
|
||||
Settings.data.systemMonitor.diskAvailWarningThreshold = value;
|
||||
if (Settings.data.systemMonitor.diskAvailCriticalThreshold > value) {
|
||||
Settings.data.systemMonitor.diskAvailCriticalThreshold = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSpinBox {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
from: 0
|
||||
to: 20
|
||||
stepSize: 5
|
||||
value: Settings.data.systemMonitor.diskAvailCriticalThreshold
|
||||
defaultValue: Settings.getDefaultValue("systemMonitor.diskAvailCriticalThreshold")
|
||||
suffix: "%"
|
||||
onValueChanged: Settings.data.systemMonitor.diskAvailCriticalThreshold = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,13 +138,15 @@ SmartPanel {
|
||||
values: SystemStatService.cpuHistory
|
||||
values2: SystemStatService.cpuTempHistory
|
||||
minValue: 0
|
||||
maxValue: Math.max(SystemStatService.cpuHistoryMax, 1)
|
||||
maxValue: 100
|
||||
minValue2: Math.max(SystemStatService.cpuTempHistoryMin - 5, 0)
|
||||
maxValue2: Math.max(SystemStatService.cpuTempHistoryMax + 5, 1)
|
||||
autoScale: false
|
||||
color: Color.mPrimary
|
||||
color2: Color.mError
|
||||
fill: true
|
||||
fillOpacity: 0.15
|
||||
updateInterval: Settings.data.systemMonitor.cpuPollingInterval
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,7 +172,7 @@ SmartPanel {
|
||||
}
|
||||
|
||||
NText {
|
||||
text: `${Math.round(SystemStatService.memPercent)}% • ${SystemStatService.formatMemoryGb(SystemStatService.memGb).replace(/[^0-9.]/g, "")} GB`
|
||||
text: `${Math.round(SystemStatService.memPercent)}% • ${SystemStatService.formatGigabytes(SystemStatService.memGb).replace(/[^0-9.]/g, "")} GB`
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mPrimary
|
||||
}
|
||||
@@ -205,10 +207,12 @@ SmartPanel {
|
||||
Layout.fillHeight: true
|
||||
values: SystemStatService.memHistory
|
||||
minValue: 0
|
||||
maxValue: Math.max(SystemStatService.memHistoryMax, 1)
|
||||
maxValue: 100
|
||||
autoScale: false
|
||||
color: Color.mPrimary
|
||||
fill: true
|
||||
fillOpacity: 0.15
|
||||
updateInterval: Settings.data.systemMonitor.memPollingInterval
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,6 +280,7 @@ SmartPanel {
|
||||
color2: Color.mError
|
||||
fill: true
|
||||
fillOpacity: 0.15
|
||||
updateInterval: Settings.data.systemMonitor.networkPollingInterval
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,7 +296,7 @@ SmartPanel {
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.margins: Style.marginM
|
||||
spacing: Style.marginS
|
||||
spacing: Style.marginXS
|
||||
|
||||
// Load Average
|
||||
RowLayout {
|
||||
@@ -320,6 +325,33 @@ SmartPanel {
|
||||
}
|
||||
}
|
||||
|
||||
// GPU Temperature (only if available)
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
visible: SystemStatService.gpuAvailable
|
||||
|
||||
NIcon {
|
||||
icon: "gpu-temperature"
|
||||
pointSize: Style.fontSizeM
|
||||
color: Color.mPrimary
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("system-monitor.gpu-temp") + ":"
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
|
||||
NText {
|
||||
text: `${Math.round(SystemStatService.gpuTemp)}°C`
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
}
|
||||
}
|
||||
|
||||
// Disk usage
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
@@ -352,33 +384,6 @@ SmartPanel {
|
||||
}
|
||||
}
|
||||
|
||||
// GPU Temperature (only if available)
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
visible: SystemStatService.gpuAvailable
|
||||
|
||||
NIcon {
|
||||
icon: "gpu-temperature"
|
||||
pointSize: Style.fontSizeM
|
||||
color: Color.mPrimary
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("system-monitor.gpu-temp") + ":"
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
|
||||
NText {
|
||||
text: `${Math.round(SystemStatService.gpuTemp)}°C`
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
}
|
||||
}
|
||||
|
||||
// Swap details (only visible if swap is enabled)
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
@@ -398,7 +403,7 @@ SmartPanel {
|
||||
}
|
||||
|
||||
NText {
|
||||
text: `${SystemStatService.formatMemoryGb(SystemStatService.swapGb).replace(/[^0-9.]/g, "")} / ${SystemStatService.formatMemoryGb(SystemStatService.swapTotalGb).replace(/[^0-9.]/g, "")} GB`
|
||||
text: `${SystemStatService.formatGigabytes(SystemStatService.swapGb).replace(/[^0-9.]/g, "")} / ${SystemStatService.formatGigabytes(SystemStatService.swapTotalGb).replace(/[^0-9.]/g, "")} GB`
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -34,7 +34,9 @@ Item {
|
||||
onHoverCountChanged: {
|
||||
if (hoverCount > 0) {
|
||||
resumeTimer.stop();
|
||||
progressAnimation.pause();
|
||||
if (progressAnimation.running && !progressAnimation.paused) {
|
||||
progressAnimation.pause();
|
||||
}
|
||||
} else {
|
||||
resumeTimer.start();
|
||||
}
|
||||
@@ -45,7 +47,7 @@ Item {
|
||||
interval: 50
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (hoverCount === 0) {
|
||||
if (hoverCount === 0 && progressAnimation.paused) {
|
||||
progressAnimation.resume();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ PopupWindow {
|
||||
property int gridPaddingVertical: Style.marginXS // extra vertical padding for grid mode
|
||||
property int delay: 0
|
||||
property int hideDelay: 0
|
||||
property int maxWidth: 320
|
||||
property int maxWidth: 340
|
||||
|
||||
property int animationDuration: Style.animationFast
|
||||
property real animationScale: 0.85
|
||||
|
||||
@@ -12,6 +12,7 @@ Scheme types:
|
||||
- M3 schemes (tonal-spot, fruit-salad, rainbow, content): Compared with matugen
|
||||
- vibrant: Prioritizes the most saturated colors regardless of area
|
||||
- faithful: Prioritizes dominant colors by area coverage
|
||||
- dysfunctional: Like faithful but picks the 2nd most dominant color family
|
||||
- muted: Preserves hue but caps saturation low (for monochrome wallpapers)
|
||||
"""
|
||||
|
||||
@@ -115,20 +116,21 @@ def run_matugen(image_path: Path, scheme: str) -> dict | None:
|
||||
|
||||
|
||||
def analyze_vibrant_faithful_muted(image_path: Path) -> None:
|
||||
"""Analyze vibrant, faithful, and muted mode outputs."""
|
||||
"""Analyze vibrant, faithful, dysfunctional, and muted mode outputs."""
|
||||
print("\n" + "=" * 78)
|
||||
print("VIBRANT vs FAITHFUL vs MUTED COMPARISON")
|
||||
print("VIBRANT vs FAITHFUL vs DYSFUNCTIONAL vs MUTED COMPARISON")
|
||||
print("=" * 78)
|
||||
print()
|
||||
print("Vibrant: Prioritizes the most saturated colors regardless of area")
|
||||
print("Faithful: Prioritizes dominant colors by area coverage")
|
||||
print("Muted: Preserves hue but caps saturation low (monochrome wallpapers)")
|
||||
print("Vibrant: Prioritizes the most saturated colors regardless of area")
|
||||
print("Faithful: Prioritizes dominant colors by area coverage")
|
||||
print("Dysfunctional: Like faithful but picks 2nd most dominant color family")
|
||||
print("Muted: Preserves hue but caps saturation low (monochrome wallpapers)")
|
||||
print()
|
||||
print("-" * 78)
|
||||
print(f"{'Mode':<12} {'Color':<12} {'Hex':<10} {'Hue':>8} {'Chroma':>8} {'Name':<10}")
|
||||
print(f"{'Mode':<14} {'Color':<12} {'Hex':<10} {'Hue':>8} {'Chroma':>8} {'Name':<10}")
|
||||
print("-" * 78)
|
||||
|
||||
for scheme in ["vibrant", "faithful", "muted"]:
|
||||
for scheme in ["vibrant", "faithful", "dysfunctional", "muted"]:
|
||||
colors = run_our_processor(image_path, scheme)
|
||||
if not colors:
|
||||
print(f"{scheme}: Failed to get colors")
|
||||
@@ -142,40 +144,51 @@ def analyze_vibrant_faithful_muted(image_path: Path) -> None:
|
||||
try:
|
||||
hct = get_hct(hex_color)
|
||||
name = hue_to_name(hct.hue)
|
||||
print(f"{scheme:<12} {key:<12} {hex_color:<10} {hct.hue:>7.1f}° {hct.chroma:>7.1f} {name:<10}")
|
||||
print(f"{scheme:<14} {key:<12} {hex_color:<10} {hct.hue:>7.1f}° {hct.chroma:>7.1f} {name:<10}")
|
||||
except Exception as e:
|
||||
print(f"{scheme:<12} {key:<12} Error: {e}")
|
||||
print(f"{scheme:<14} {key:<12} Error: {e}")
|
||||
|
||||
print("-" * 78)
|
||||
|
||||
# Summary comparison
|
||||
vibrant = run_our_processor(image_path, "vibrant")
|
||||
faithful = run_our_processor(image_path, "faithful")
|
||||
dysfunctional = run_our_processor(image_path, "dysfunctional")
|
||||
muted = run_our_processor(image_path, "muted")
|
||||
|
||||
if vibrant and faithful and muted:
|
||||
if vibrant and faithful and dysfunctional and muted:
|
||||
print()
|
||||
print("Summary:")
|
||||
v_hct = get_hct(vibrant.get("primary", "#000000"))
|
||||
f_hct = get_hct(faithful.get("primary", "#000000"))
|
||||
d_hct = get_hct(dysfunctional.get("primary", "#000000"))
|
||||
m_hct = get_hct(muted.get("primary", "#000000"))
|
||||
|
||||
v_name = hue_to_name(v_hct.hue)
|
||||
f_name = hue_to_name(f_hct.hue)
|
||||
d_name = hue_to_name(d_hct.hue)
|
||||
m_name = hue_to_name(m_hct.hue)
|
||||
|
||||
vf_diff = hue_diff(v_hct.hue, f_hct.hue)
|
||||
fd_diff = hue_diff(f_hct.hue, d_hct.hue)
|
||||
|
||||
print(f" Vibrant primary: {vibrant.get('primary')} ({v_name}, hue {v_hct.hue:.0f}°, chroma {v_hct.chroma:.1f})")
|
||||
print(f" Faithful primary: {faithful.get('primary')} ({f_name}, hue {f_hct.hue:.0f}°, chroma {f_hct.chroma:.1f})")
|
||||
print(f" Muted primary: {muted.get('primary')} ({m_name}, hue {m_hct.hue:.0f}°, chroma {m_hct.chroma:.1f})")
|
||||
print(f" V-F hue diff: {vf_diff:.1f}°")
|
||||
print(f" Vibrant primary: {vibrant.get('primary')} ({v_name}, hue {v_hct.hue:.0f}°, chroma {v_hct.chroma:.1f})")
|
||||
print(f" Faithful primary: {faithful.get('primary')} ({f_name}, hue {f_hct.hue:.0f}°, chroma {f_hct.chroma:.1f})")
|
||||
print(f" Dysfunctional primary:{dysfunctional.get('primary')} ({d_name}, hue {d_hct.hue:.0f}°, chroma {d_hct.chroma:.1f})")
|
||||
print(f" Muted primary: {muted.get('primary')} ({m_name}, hue {m_hct.hue:.0f}°, chroma {m_hct.chroma:.1f})")
|
||||
print(f" V-F hue diff: {vf_diff:.1f}°")
|
||||
print(f" F-D hue diff: {fd_diff:.1f}°")
|
||||
|
||||
if vf_diff > 60:
|
||||
print(f" → Vibrant/Faithful picked DIFFERENT color families ({v_name} vs {f_name})")
|
||||
else:
|
||||
print(f" → Vibrant/Faithful picked SIMILAR colors")
|
||||
|
||||
if fd_diff > 30:
|
||||
print(f" → Faithful/Dysfunctional picked DIFFERENT color families ({f_name} vs {d_name})")
|
||||
else:
|
||||
print(f" → Faithful/Dysfunctional picked SIMILAR colors (may only have 1 dominant family)")
|
||||
|
||||
# Note the muted chroma reduction
|
||||
if m_hct.chroma < 20:
|
||||
print(f" → Muted successfully reduced chroma to {m_hct.chroma:.1f}")
|
||||
|
||||
@@ -31,11 +31,35 @@ from wayland import protocol as wp
|
||||
from wayland.client import MakeDisplay, ServerDisconnected, NoXDGRuntimeDir
|
||||
|
||||
# Protocol XML paths
|
||||
WAYLAND_XML = '/usr/share/wayland/wayland.xml'
|
||||
PROTOCOLS_DIR = os.path.join(VENDOR_DIR, 'wayland', 'protocols')
|
||||
EXT_WORKSPACE_XML = os.path.join(PROTOCOLS_DIR, 'ext-workspace-v1.xml')
|
||||
|
||||
|
||||
def find_wayland_xml():
|
||||
"""Find wayland.xml using XDG_DATA_DIRS"""
|
||||
# Get XDG_DATA_DIRS, falling back to standard paths
|
||||
xdg_data_dirs = os.environ.get('XDG_DATA_DIRS', '/usr/local/share:/usr/share')
|
||||
|
||||
for data_dir in xdg_data_dirs.split(':'):
|
||||
wayland_xml = os.path.join(data_dir, 'wayland', 'wayland.xml')
|
||||
if os.path.exists(wayland_xml):
|
||||
return wayland_xml
|
||||
|
||||
# Fallback to common paths if not found in XDG_DATA_DIRS
|
||||
fallback_paths = [
|
||||
'/usr/share/wayland/wayland.xml',
|
||||
'/usr/local/share/wayland/wayland.xml',
|
||||
]
|
||||
for path in fallback_paths:
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
|
||||
return None
|
||||
|
||||
|
||||
WAYLAND_XML = find_wayland_xml()
|
||||
|
||||
|
||||
class WorkspaceState:
|
||||
"""Tracks the current state of all workspaces"""
|
||||
|
||||
@@ -349,10 +373,10 @@ def main():
|
||||
args = parser.parse_args()
|
||||
|
||||
# Check for required protocol files
|
||||
if not os.path.exists(WAYLAND_XML):
|
||||
if not WAYLAND_XML or not os.path.exists(WAYLAND_XML):
|
||||
print(json.dumps({
|
||||
'type': 'error',
|
||||
'message': f'Wayland protocol file not found: {WAYLAND_XML}'
|
||||
'message': 'Wayland protocol file not found. Check XDG_DATA_DIRS or install wayland-devel.'
|
||||
}), flush=True)
|
||||
return 1
|
||||
|
||||
|
||||
@@ -250,19 +250,22 @@ def _read_image_imagemagick(path: Path) -> list[RGB]:
|
||||
|
||||
# Resize to 112x112 to match matugen's color extraction
|
||||
# Use -filter Box for consistent results across ImageMagick versions
|
||||
# Use -depth 8 -colorspace sRGB -strip to reduce variance between HDRI/non-HDRI builds
|
||||
resize_spec = "112x112!"
|
||||
|
||||
try:
|
||||
# Try 'magick convert' first (ImageMagick 7+), fallback to 'convert' (ImageMagick 6)
|
||||
# Try 'magick' first (ImageMagick 7+), fallback to 'convert' (ImageMagick 6)
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['magick', 'convert', str(path), '-filter', 'Box', '-resize', resize_spec, '-depth', '8', 'ppm:-'],
|
||||
['magick', str(path), '-filter', 'Box', '-resize', resize_spec,
|
||||
'-depth', '8', '-colorspace', 'sRGB', '-strip', 'ppm:-'],
|
||||
capture_output=True,
|
||||
check=True
|
||||
)
|
||||
except FileNotFoundError:
|
||||
result = subprocess.run(
|
||||
['convert', str(path), '-filter', 'Box', '-resize', resize_spec, '-depth', '8', 'ppm:-'],
|
||||
['convert', str(path), '-filter', 'Box', '-resize', resize_spec,
|
||||
'-depth', '8', '-colorspace', 'sRGB', '-strip', 'ppm:-'],
|
||||
capture_output=True,
|
||||
check=True
|
||||
)
|
||||
|
||||
@@ -262,6 +262,129 @@ def _score_colors_count(
|
||||
return result_colors
|
||||
|
||||
|
||||
def _family_center_hue(family: int) -> float:
|
||||
"""Get the center hue for a family index."""
|
||||
# Family centers based on _hue_to_family ranges:
|
||||
# 0: RED (330-30°, wraps) -> center 0°
|
||||
# 1: ORANGE (30-60°) -> center 45°
|
||||
# 2: YELLOW (60-105°) -> center 82.5°
|
||||
# 3: GREEN (105-190°) -> center 147.5°
|
||||
# 4: BLUE (190-270°) -> center 230°
|
||||
# 5: PURPLE (270-330°) -> center 300°
|
||||
centers = [0.0, 45.0, 82.5, 147.5, 230.0, 300.0]
|
||||
return centers[family]
|
||||
|
||||
|
||||
def _circular_hue_diff(h1: float, h2: float) -> float:
|
||||
"""Calculate circular hue difference (0-180)."""
|
||||
diff = abs(h1 - h2)
|
||||
return min(diff, 360.0 - diff)
|
||||
|
||||
|
||||
def _score_colors_dysfunctional(
|
||||
colors_with_counts: list[tuple[RGB, int]],
|
||||
) -> list[tuple[Color, float]]:
|
||||
"""
|
||||
Score colors prioritizing the 2nd most dominant hue family.
|
||||
|
||||
Like count scoring but skips the dominant family (and any families
|
||||
too close to it) to pick a visually distinct secondary color.
|
||||
|
||||
Args:
|
||||
colors_with_counts: List of (RGB, count) tuples from clustering
|
||||
|
||||
Returns:
|
||||
List of (Color, score) tuples, sorted by family dominance then count
|
||||
"""
|
||||
MIN_CHROMA = 10.0 # Filter out near-gray colors
|
||||
MIN_HUE_DISTANCE = 45.0 # Minimum hue distance from dominant family
|
||||
MIN_COUNT_RATIO = 0.02 # Distant family must have at least 2% of total colorful pixels
|
||||
|
||||
# First pass: collect colorful colors and group by hue family
|
||||
hue_families: dict[int, list[tuple[Color, float, float, int]]] = {} # family -> [(color, hue, chroma, count), ...]
|
||||
|
||||
for rgb, count in colors_with_counts:
|
||||
color = Color.from_rgb(rgb)
|
||||
try:
|
||||
hct = color.to_hct()
|
||||
if hct.chroma >= MIN_CHROMA:
|
||||
family = _hue_to_family(hct.hue)
|
||||
if family not in hue_families:
|
||||
hue_families[family] = []
|
||||
hue_families[family].append((color, hct.hue, hct.chroma, count))
|
||||
except (ValueError, ZeroDivisionError):
|
||||
pass
|
||||
|
||||
# If no colorful colors found, fall back to all colors
|
||||
if not hue_families:
|
||||
result = []
|
||||
for rgb, count in colors_with_counts:
|
||||
color = Color.from_rgb(rgb)
|
||||
result.append((color, float(count)))
|
||||
result.sort(key=lambda x: -x[1])
|
||||
return result
|
||||
|
||||
# Calculate total count per hue family
|
||||
family_totals: list[tuple[int, int]] = []
|
||||
for family, colors in hue_families.items():
|
||||
total = sum(c[3] for c in colors)
|
||||
family_totals.append((family, total))
|
||||
|
||||
# Sort families by total count (dominant family first)
|
||||
family_totals.sort(key=lambda x: -x[1])
|
||||
|
||||
# Find the dominant family and its center hue
|
||||
dominant_family, dominant_count = family_totals[0]
|
||||
dominant_center = _family_center_hue(dominant_family)
|
||||
total_colorful_pixels = sum(count for _, count in family_totals)
|
||||
min_count = total_colorful_pixels * MIN_COUNT_RATIO
|
||||
|
||||
# Find families that are far enough from the dominant one AND have enough pixels
|
||||
distant_families = []
|
||||
close_families = [dominant_family]
|
||||
for family, count in family_totals[1:]:
|
||||
family_center = _family_center_hue(family)
|
||||
hue_diff = _circular_hue_diff(dominant_center, family_center)
|
||||
if hue_diff >= MIN_HUE_DISTANCE and count >= min_count:
|
||||
# Get max chroma in this family - we want families with vibrant colors
|
||||
max_chroma = max(c[2] for c in hue_families[family])
|
||||
distant_families.append((family, count, hue_diff, max_chroma))
|
||||
else:
|
||||
close_families.append(family)
|
||||
|
||||
# Build result: colors from distant families first
|
||||
result_colors = []
|
||||
|
||||
# Sort distant families by weighted score: hue_distance * max_chroma
|
||||
# This balances visual distinctness (hue distance) with color quality (chroma)
|
||||
# A family that's far away AND has good colors beats one that's close with great colors
|
||||
distant_families.sort(key=lambda x: -(x[2] * x[3]))
|
||||
|
||||
for family, _, _, _ in distant_families:
|
||||
family_colors = hue_families[family]
|
||||
# Sort by chroma descending - we want the most vibrant color from this family
|
||||
# Count is tiebreaker to avoid picking tiny noise clusters
|
||||
family_colors.sort(key=lambda x: (-x[2], -x[3]))
|
||||
for color, hue, chroma, count in family_colors:
|
||||
# Score encodes family rank + chroma for proper ordering
|
||||
# Chroma is primary (we want vibrant), count is tiebreaker
|
||||
family_rank = next(i for i, (f, _, _, _) in enumerate(distant_families) if f == family)
|
||||
score = (len(distant_families) - family_rank) * 1000000 + chroma * 1000 + count
|
||||
result_colors.append((color, score))
|
||||
|
||||
# Add colors from close families (including dominant) at lower priority
|
||||
for family in close_families:
|
||||
family_colors = hue_families[family]
|
||||
family_colors.sort(key=lambda x: (-x[3], -x[2]))
|
||||
for color, hue, chroma, count in family_colors:
|
||||
# Lower score than all distant-family colors
|
||||
score = count * 1000 + chroma
|
||||
result_colors.append((color, score))
|
||||
|
||||
result_colors.sort(key=lambda x: -x[1])
|
||||
return result_colors
|
||||
|
||||
|
||||
def _score_colors_muted(
|
||||
colors_with_counts: list[tuple[RGB, int]],
|
||||
) -> list[tuple[Color, float]]:
|
||||
@@ -434,6 +557,7 @@ def extract_palette(
|
||||
- "population": matugen-like, representative colors (M3 schemes)
|
||||
- "chroma": vibrant, chroma-prioritized with centroid averaging
|
||||
- "count": area-dominant, picks by pixel count (faithful mode)
|
||||
- "dysfunctional": picks 2nd most dominant color family
|
||||
- "muted": like count but without chroma filtering (monochrome wallpapers)
|
||||
|
||||
Returns:
|
||||
@@ -456,6 +580,10 @@ def extract_palette(
|
||||
# Scoring will filter to colorful colors and pick by count
|
||||
cluster_count = 48
|
||||
filtered = sampled
|
||||
elif scoring == "dysfunctional":
|
||||
# Dysfunctional mode: same as count but picks 2nd dominant family
|
||||
cluster_count = 48
|
||||
filtered = sampled
|
||||
elif scoring == "muted":
|
||||
# Muted mode: similar to count but accepts low-chroma colors
|
||||
# For monochrome/monotonal wallpapers
|
||||
@@ -494,6 +622,10 @@ def extract_palette(
|
||||
# Use representative colors with count scoring (faithful mode)
|
||||
colors_for_scoring = [(c[1], c[2]) for c in clusters]
|
||||
scored = _score_colors_count(colors_for_scoring)
|
||||
elif scoring == "dysfunctional":
|
||||
# Use representative colors with dysfunctional scoring (2nd dominant family)
|
||||
colors_for_scoring = [(c[1], c[2]) for c in clusters]
|
||||
scored = _score_colors_dysfunctional(colors_for_scoring)
|
||||
elif scoring == "muted":
|
||||
# Use representative colors with muted scoring (no chroma filter)
|
||||
colors_for_scoring = [(c[1], c[2]) for c in clusters]
|
||||
|
||||
@@ -853,14 +853,14 @@ def generate_theme(
|
||||
Args:
|
||||
palette: List of extracted colors
|
||||
mode: "dark" or "light"
|
||||
scheme_type: One of "tonal-spot", "fruit-salad", "rainbow", "vibrant", "faithful", "muted"
|
||||
scheme_type: One of "tonal-spot", "fruit-salad", "rainbow", "vibrant", "faithful", "dysfunctional", "muted"
|
||||
|
||||
Returns:
|
||||
Dictionary of color token names to hex values
|
||||
"""
|
||||
# Handle vibrant/faithful modes (use generate_normal_* functions)
|
||||
# Both use same theme generation, but different color extraction (handled in palette.py)
|
||||
if scheme_type in ("vibrant", "faithful"):
|
||||
# Handle vibrant/faithful/dysfunctional modes (use generate_normal_* functions)
|
||||
# All three use same theme generation, but different color extraction (handled in palette.py)
|
||||
if scheme_type in ("vibrant", "faithful", "dysfunctional"):
|
||||
if mode == "dark":
|
||||
return generate_normal_dark(palette)
|
||||
return generate_normal_light(palette)
|
||||
|
||||
@@ -12,13 +12,14 @@ Supported scheme types:
|
||||
- monochrome: Pure grayscale M3 scheme (chroma = 0, only error has color)
|
||||
- vibrant: Prioritizes the most saturated colors regardless of area coverage
|
||||
- faithful: Prioritizes dominant colors by area, what you see is what you get
|
||||
- dysfunctional: Like faithful but picks the 2nd most dominant color family
|
||||
- muted: Preserves hue but caps saturation low (for monochrome/monotonal wallpapers)
|
||||
|
||||
Usage:
|
||||
python3 template-processor.py IMAGE_OR_JSON [OPTIONS]
|
||||
|
||||
Options:
|
||||
--scheme-type Scheme type: tonal-spot (default), content, fruit-salad, rainbow, monochrome, vibrant, faithful, muted
|
||||
--scheme-type Scheme type: tonal-spot (default), content, fruit-salad, rainbow, monochrome, vibrant, faithful, dysfunctional, muted
|
||||
--dark Generate dark theme only
|
||||
--light Generate light theme only
|
||||
--both Generate both themes (default)
|
||||
@@ -83,7 +84,7 @@ Examples:
|
||||
# Scheme type selection
|
||||
parser.add_argument(
|
||||
'--scheme-type',
|
||||
choices=['tonal-spot', 'content', 'fruit-salad', 'rainbow', 'monochrome', 'vibrant', 'faithful', 'muted'],
|
||||
choices=['tonal-spot', 'content', 'fruit-salad', 'rainbow', 'monochrome', 'vibrant', 'faithful', 'dysfunctional', 'muted'],
|
||||
default='tonal-spot',
|
||||
help='Color scheme type (default: tonal-spot)'
|
||||
)
|
||||
@@ -267,6 +268,7 @@ def main() -> int:
|
||||
# This matches matugen's color extraction exactly
|
||||
# - vibrant: Use k-means clustering for colorful/blended colors
|
||||
# - faithful: Use Wu quantizer for primary (dominant by area), k-means for accents
|
||||
# - dysfunctional: Like faithful but picks 2nd most dominant color family
|
||||
# - muted: Like count but without chroma filtering (for monochrome wallpapers)
|
||||
if scheme_type == "vibrant":
|
||||
# K-means with chroma scoring for vibrant, blended colors
|
||||
@@ -275,6 +277,10 @@ def main() -> int:
|
||||
# K-means with count scoring - picks dominant color by area coverage
|
||||
# This ensures primary reflects what you actually see in the image
|
||||
palette = extract_palette(pixels, k=5, scoring="count")
|
||||
elif scheme_type == "dysfunctional":
|
||||
# K-means with dysfunctional scoring - picks 2nd most dominant color family
|
||||
# For when the dominant color is not what you want as primary
|
||||
palette = extract_palette(pixels, k=5, scoring="dysfunctional")
|
||||
elif scheme_type == "muted":
|
||||
# K-means with muted scoring - accepts low/zero chroma colors
|
||||
# For monochrome/monotonal wallpapers where dominant color has low saturation
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env python3
|
||||
# Finds all installed Noctalia theme extensions for VSCode/VSCodium.
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def find_all_noctalia_themes(extensions_dir: Path, prefix: str) -> list[str]:
|
||||
# Bail early if the extensions directory doesn't exist
|
||||
if not extensions_dir.is_dir():
|
||||
return []
|
||||
# Collect all directories matching the extension prefix
|
||||
candidates = [d for d in extensions_dir.iterdir() if d.is_dir() and d.name.startswith(prefix)]
|
||||
# Return theme file paths for all matching extensions
|
||||
return [str(d / "themes" / "NoctaliaTheme-color-theme.json") for d in candidates]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Resolve ~ in the provided extensions directory path
|
||||
extensions_dir = Path(sys.argv[1]).expanduser()
|
||||
prefix = sys.argv[2] if len(sys.argv) > 2 else "noctalia.noctaliatheme-"
|
||||
|
||||
# Print the resolved paths to stdout for the QML Process to capture
|
||||
results = find_all_noctalia_themes(extensions_dir, prefix)
|
||||
if results:
|
||||
for path in results:
|
||||
print(path)
|
||||
else:
|
||||
print(f"No matching extension found in {extensions_dir}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
@@ -178,8 +178,9 @@ Item {
|
||||
|
||||
for (var i = 0; i < hlWorkspaces.length; i++) {
|
||||
const ws = hlWorkspaces[i];
|
||||
if (!ws || ws.id < 1)
|
||||
if (ws.name && ws.name.startsWith("special:"))
|
||||
continue;
|
||||
|
||||
const wsData = {
|
||||
"id": ws.id,
|
||||
"idx": ws.id,
|
||||
@@ -428,6 +429,10 @@ Item {
|
||||
// Public functions
|
||||
function switchToWorkspace(workspace) {
|
||||
try {
|
||||
if (workspace.name) {
|
||||
Hyprland.dispatch(`workspace ${workspace.name}`);
|
||||
return;
|
||||
}
|
||||
Hyprland.dispatch(`workspace ${workspace.idx}`);
|
||||
} catch (e) {
|
||||
Logger.e("HyprlandService", "Failed to switch workspace:", e);
|
||||
|
||||
@@ -692,7 +692,7 @@ Item {
|
||||
function spawn(command) {
|
||||
try {
|
||||
const cmdStr = Array.isArray(command) ? command.join(" ") : command;
|
||||
Quickshell.execDetached(["mmsg", "-d", "'spawn," + cmdStr + "'"]);
|
||||
Quickshell.execDetached(["sh", "-c", "mmsg -d 'spawn," + cmdStr + "'"]);
|
||||
} catch (e) {
|
||||
Logger.e("MangoService", "Failed to spawn command:", e);
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ Singleton {
|
||||
|
||||
function runPowerHook(script, callback) {
|
||||
pendingPowerCallback = callback;
|
||||
powerHookProcess.command = ["sh", "-c", script];
|
||||
powerHookProcess.command = ["sh", "-lc", script];
|
||||
powerHookProcess.running = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ Singleton {
|
||||
// For internal displays, query the system directly
|
||||
refreshProc.command = ["sh", "-c", "cat " + monitor.brightnessPath + " && " + "cat " + monitor.maxBrightnessPath];
|
||||
refreshProc.running = true;
|
||||
} else if (monitor.isDdc) {
|
||||
} else if (monitor.isDdc && monitor.busNum !== "") {
|
||||
// For DDC displays, get the current value
|
||||
refreshProc.command = ["ddcutil", "-b", monitor.busNum, "--sleep-multiplier=0.05", "getvcp", "10", "--brief"];
|
||||
refreshProc.running = true;
|
||||
@@ -362,17 +362,20 @@ Singleton {
|
||||
}
|
||||
|
||||
// Execute the brightness change command
|
||||
monitor.commandRunning = true;
|
||||
monitor.ignoreNextChange = true;
|
||||
|
||||
if (isAppleDisplay) {
|
||||
monitor.commandRunning = true;
|
||||
monitor.ignoreNextChange = true;
|
||||
setBrightnessProc.command = ["asdbctl", "set", rounded];
|
||||
setBrightnessProc.running = true;
|
||||
} else if (isDdc) {
|
||||
} else if (isDdc && busNum !== "") {
|
||||
monitor.commandRunning = true;
|
||||
monitor.ignoreNextChange = true;
|
||||
var ddcValue = Math.round(value * monitor.maxBrightness);
|
||||
setBrightnessProc.command = ["ddcutil", "-b", busNum, "--noverify", "--async", "--sleep-multiplier=0.05", "setvcp", "10", ddcValue];
|
||||
setBrightnessProc.running = true;
|
||||
} else {
|
||||
} else if (!isDdc) {
|
||||
monitor.commandRunning = true;
|
||||
monitor.ignoreNextChange = true;
|
||||
setBrightnessProc.command = ["brightnessctl", "s", rounded + "%"];
|
||||
setBrightnessProc.running = true;
|
||||
}
|
||||
@@ -381,14 +384,16 @@ Singleton {
|
||||
function initBrightness(): void {
|
||||
if (isAppleDisplay) {
|
||||
initProc.command = ["asdbctl", "get"];
|
||||
} else if (isDdc) {
|
||||
initProc.running = true;
|
||||
} else if (isDdc && busNum !== "") {
|
||||
initProc.command = ["ddcutil", "-b", busNum, "--sleep-multiplier=0.05", "getvcp", "10", "--brief"];
|
||||
} else {
|
||||
initProc.running = true;
|
||||
} else if (!isDdc) {
|
||||
// Internal backlight - find the first available backlight device and get its info
|
||||
// This now returns: device_path, current_brightness, max_brightness (on separate lines)
|
||||
initProc.command = ["sh", "-c", "for dev in /sys/class/backlight/*; do " + " if [ -f \"$dev/brightness\" ] && [ -f \"$dev/max_brightness\" ]; then " + " echo \"$dev\"; " + " cat \"$dev/brightness\"; " + " cat \"$dev/max_brightness\"; " + " break; " + " fi; " + "done"];
|
||||
initProc.running = true;
|
||||
}
|
||||
initProc.running = true;
|
||||
}
|
||||
|
||||
onBusNumChanged: initBrightness()
|
||||
|
||||
@@ -291,11 +291,11 @@ Singleton {
|
||||
watchersStarted = true;
|
||||
|
||||
// Text watcher
|
||||
watchText.command = ["sh", "-lc", Settings.data.appLauncher.clipboardWatchTextCommand];
|
||||
watchText.command = ["sh", "-c", Settings.data.appLauncher.clipboardWatchTextCommand];
|
||||
watchText.running = true;
|
||||
|
||||
// Image watcher
|
||||
watchImage.command = ["sh", "-lc", Settings.data.appLauncher.clipboardWatchImageCommand];
|
||||
watchImage.command = ["sh", "-c", Settings.data.appLauncher.clipboardWatchImageCommand];
|
||||
watchImage.running = true;
|
||||
}
|
||||
|
||||
@@ -407,7 +407,7 @@ Singleton {
|
||||
root._b64CurrentCb = job.cb;
|
||||
root._b64CurrentMime = job.mime;
|
||||
root._b64CurrentId = job.id;
|
||||
decodeB64Proc.command = ["sh", "-lc", `cliphist decode ${job.id} | base64 -w 0`];
|
||||
decodeB64Proc.command = ["sh", "-c", `cliphist decode ${job.id} | base64 -w 0`];
|
||||
decodeB64Proc.running = true;
|
||||
}
|
||||
|
||||
@@ -415,7 +415,7 @@ Singleton {
|
||||
if (!root.cliphistAvailable) {
|
||||
return;
|
||||
}
|
||||
copyProc.command = ["sh", "-lc", `cliphist decode ${id} | wl-copy`];
|
||||
copyProc.command = ["sh", "-c", `cliphist decode ${id} | wl-copy`];
|
||||
copyProc.running = true;
|
||||
}
|
||||
|
||||
@@ -427,7 +427,7 @@ Singleton {
|
||||
const typeArg = isImage ? ` --type ${mime}` : "";
|
||||
const pasteKeys = isImage ? "wtype -M ctrl -k v" : "wtype -M ctrl -M shift v";
|
||||
const cmd = `cliphist decode ${id} | wl-copy${typeArg} && ${pasteKeys}`;
|
||||
pasteProc.command = ["sh", "-lc", cmd];
|
||||
pasteProc.command = ["sh", "-c", cmd];
|
||||
pasteProc.running = true;
|
||||
}
|
||||
|
||||
@@ -436,7 +436,7 @@ Singleton {
|
||||
return;
|
||||
const escaped = text.replace(/'/g, "'\\''");
|
||||
const cmd = `printf '%s' '${escaped}' | wl-copy && wtype -M ctrl -M shift v`;
|
||||
pasteProc.command = ["sh", "-lc", cmd];
|
||||
pasteProc.command = ["sh", "-c", cmd];
|
||||
pasteProc.running = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1446,7 +1446,9 @@ Singleton {
|
||||
}
|
||||
|
||||
// Try to find the plugin panel slot (pluginPanel1 or pluginPanel2)
|
||||
// Try slot 1 first, then slot 2
|
||||
// Priority: 1) toggle same plugin, 2) empty slot, 3) closed slot, 4) replace open slot
|
||||
var closedSlot = null;
|
||||
|
||||
for (var slotNum = 1; slotNum <= 2; slotNum++) {
|
||||
var panelName = "pluginPanel" + slotNum;
|
||||
var panel = PanelService.getPanel(panelName, screen);
|
||||
@@ -1460,22 +1462,38 @@ Singleton {
|
||||
|
||||
// If this slot is empty, use it
|
||||
if (panel.currentPluginId === "") {
|
||||
// Set the pluginId first - when panel opens and panelContent loads,
|
||||
// Component.onCompleted will call loadPluginPanel automatically
|
||||
panel.currentPluginId = pluginId;
|
||||
panel.open(buttonItem);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Track first closed slot (panel assigned but not showing)
|
||||
if (!closedSlot && !panel.isPanelOpen) {
|
||||
closedSlot = panel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If both slots are occupied, use slot 1 (replace existing)
|
||||
// Prefer reusing a closed slot over replacing an open one
|
||||
if (closedSlot) {
|
||||
closedSlot.currentPluginId = pluginId;
|
||||
closedSlot.open(buttonItem);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If both slots are occupied and open, use slot 1 (replace existing)
|
||||
var panel1 = PanelService.getPanel("pluginPanel1", screen);
|
||||
if (panel1) {
|
||||
var wasAlreadyOpen = panel1.isPanelOpen;
|
||||
panel1.unloadPluginPanel();
|
||||
// Set the pluginId first - when panel opens and panelContent loads,
|
||||
// Component.onCompleted will call loadPluginPanel automatically
|
||||
panel1.currentPluginId = pluginId;
|
||||
|
||||
// If panel was already open, Component.onCompleted won't fire again
|
||||
// since panelContent is already loaded. We need to load the plugin manually.
|
||||
if (wasAlreadyOpen && panel1.contentLoader) {
|
||||
panel1.loadPluginPanel(pluginId);
|
||||
}
|
||||
|
||||
panel1.open(buttonItem);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ Singleton {
|
||||
id: root
|
||||
|
||||
// Version properties
|
||||
readonly property string baseVersion: "4.3.1"
|
||||
readonly property string baseVersion: "4.3.2"
|
||||
readonly property bool isDevelopment: true
|
||||
readonly property string developmentSuffix: "-git"
|
||||
readonly property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + developmentSuffix}`
|
||||
|
||||
@@ -32,7 +32,9 @@ Singleton {
|
||||
property real swapPercent: 0
|
||||
property real swapTotalGb: 0
|
||||
property var diskPercents: ({})
|
||||
property var diskAvailPercents: ({}) // available disk space in percent
|
||||
property var diskUsedGb: ({}) // Used space in GB per mount point
|
||||
property var diskAvailableGb: ({}) // available space in GB per mount point
|
||||
property var diskSizeGb: ({}) // Total size in GB per mount point
|
||||
property var diskAvailGb: ({})
|
||||
property real rxSpeed: 0
|
||||
@@ -70,7 +72,7 @@ Singleton {
|
||||
property real gpuTempHistoryMin: 100
|
||||
property real gpuTempHistoryMax: 0
|
||||
property real memHistoryMax: 0
|
||||
// Network uses existing rxMaxSpeed/txMaxSpeed (7-day learned peaks)
|
||||
// Network uses autoscaling from current history window
|
||||
// Disk is always 0-100%
|
||||
|
||||
// History management - called from update functions, not change handlers
|
||||
@@ -150,17 +152,17 @@ Singleton {
|
||||
txSpeedHistory = txH;
|
||||
}
|
||||
|
||||
// Network max speed tracking (learned over time, cached for 7 days)
|
||||
// Network max speed tracking (autoscales from current history window)
|
||||
readonly property real rxMaxSpeed: {
|
||||
const peaks = networkStatsAdapter.rxPeaks || [];
|
||||
return peaks.length > 0 ? Math.max(...peaks.map(p => p.speed)) : 0;
|
||||
const max = Math.max(...rxSpeedHistory);
|
||||
return max > 0 ? max : 1024; // Minimum 1 KB/s floor
|
||||
}
|
||||
readonly property real txMaxSpeed: {
|
||||
const peaks = networkStatsAdapter.txPeaks || [];
|
||||
return peaks.length > 0 ? Math.max(...peaks.map(p => p.speed)) : 0;
|
||||
const max = Math.max(...txSpeedHistory);
|
||||
return max > 0 ? max : 1024; // Minimum 1 KB/s floor
|
||||
}
|
||||
|
||||
// Ready-to-use ratios based on learned maximums (0..1 range)
|
||||
// Ready-to-use ratios based on current maximums (0..1 range)
|
||||
readonly property real rxRatio: rxMaxSpeed > 0 ? Math.min(1, rxSpeed / rxMaxSpeed) : 0
|
||||
readonly property real txRatio: txMaxSpeed > 0 ? Math.min(1, txSpeed / txMaxSpeed) : 0
|
||||
|
||||
@@ -181,6 +183,8 @@ Singleton {
|
||||
readonly property int swapCriticalThreshold: Settings.data.systemMonitor.swapCriticalThreshold
|
||||
readonly property int diskWarningThreshold: Settings.data.systemMonitor.diskWarningThreshold
|
||||
readonly property int diskCriticalThreshold: Settings.data.systemMonitor.diskCriticalThreshold
|
||||
readonly property int diskAvailWarningThreshold: Settings.data.systemMonitor.diskAvailWarningThreshold
|
||||
readonly property int diskAvailCriticalThreshold: Settings.data.systemMonitor.diskAvailCriticalThreshold
|
||||
|
||||
// Computed warning/critical states (uses >= inclusive comparison)
|
||||
readonly property bool cpuWarning: cpuUsage >= cpuWarningThreshold
|
||||
@@ -195,12 +199,12 @@ Singleton {
|
||||
readonly property bool swapCritical: swapPercent >= swapCriticalThreshold
|
||||
|
||||
// Helper functions for disk (disk path is dynamic)
|
||||
function isDiskWarning(diskPath) {
|
||||
return (diskPercents[diskPath] || 0) >= diskWarningThreshold;
|
||||
function isDiskWarning(diskPath, available = false) {
|
||||
return available ? (diskAvailPercents[diskPath] || 0) <= diskAvailWarningThreshold : (diskPercents[diskPath] || 0) >= diskWarningThreshold;
|
||||
}
|
||||
|
||||
function isDiskCritical(diskPath) {
|
||||
return (diskPercents[diskPath] || 0) >= diskCriticalThreshold;
|
||||
function isDiskCritical(diskPath, available = false) {
|
||||
return available ? (diskAvailPercents[diskPath] || 0) <= diskAvailCriticalThreshold : (diskPercents[diskPath] || 0) >= diskCriticalThreshold;
|
||||
}
|
||||
|
||||
// Ready-to-use stat colors (for gauges, panels, icons)
|
||||
@@ -210,8 +214,8 @@ Singleton {
|
||||
readonly property color memColor: memCritical ? criticalColor : (memWarning ? warningColor : Color.mPrimary)
|
||||
readonly property color swapColor: swapCritical ? criticalColor : (swapWarning ? warningColor : Color.mPrimary)
|
||||
|
||||
function getDiskColor(diskPath) {
|
||||
return isDiskCritical(diskPath) ? criticalColor : (isDiskWarning(diskPath) ? warningColor : Color.mPrimary);
|
||||
function getDiskColor(diskPath, available = false) {
|
||||
return isDiskCritical(diskPath, available) ? criticalColor : (isDiskWarning(diskPath, available) ? warningColor : Color.mPrimary);
|
||||
}
|
||||
|
||||
// Helper function for color resolution based on value and thresholds
|
||||
@@ -251,52 +255,6 @@ Singleton {
|
||||
property var foundGpuSensors: [] // [{hwmonPath, type, hasDedicatedVram}]
|
||||
property int gpuVramCheckIndex: 0
|
||||
|
||||
// --------------------------------------------
|
||||
// Network speed stats cache (7-day rolling window)
|
||||
property string networkStatsFile: Settings.cacheDir + "network_stats.json"
|
||||
|
||||
FileView {
|
||||
id: networkStatsView
|
||||
path: root.networkStatsFile
|
||||
printErrors: false
|
||||
|
||||
JsonAdapter {
|
||||
id: networkStatsAdapter
|
||||
property var rxPeaks: []
|
||||
property var txPeaks: []
|
||||
}
|
||||
|
||||
onLoadFailed: {
|
||||
networkStatsAdapter.rxPeaks = [];
|
||||
networkStatsAdapter.txPeaks = [];
|
||||
}
|
||||
|
||||
onLoaded: {
|
||||
root.pruneExpiredPeaks();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: networkStatsSaveDebounce
|
||||
interval: 1000
|
||||
onTriggered: networkStatsView.writeAdapter()
|
||||
}
|
||||
|
||||
function pruneExpiredPeaks() {
|
||||
const sevenDaysMs = 7 * 24 * 60 * 60 * 1000;
|
||||
const cutoff = Date.now() - sevenDaysMs;
|
||||
const rxBefore = (networkStatsAdapter.rxPeaks || []).length;
|
||||
const txBefore = (networkStatsAdapter.txPeaks || []).length;
|
||||
|
||||
networkStatsAdapter.rxPeaks = (networkStatsAdapter.rxPeaks || []).filter(p => p.timestamp > cutoff);
|
||||
networkStatsAdapter.txPeaks = (networkStatsAdapter.txPeaks || []).filter(p => p.timestamp > cutoff);
|
||||
|
||||
// Save if any were pruned
|
||||
if (networkStatsAdapter.rxPeaks.length !== rxBefore || networkStatsAdapter.txPeaks.length !== txBefore) {
|
||||
networkStatsSaveDebounce.restart();
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
Component.onCompleted: {
|
||||
Logger.i("SystemStat", "Service started with custom polling intervals");
|
||||
@@ -490,9 +448,10 @@ Singleton {
|
||||
onStreamFinished: {
|
||||
const lines = text.trim().split('\n');
|
||||
const newPercents = {};
|
||||
const newAvailPercents = {};
|
||||
const newUsedGb = {};
|
||||
const newSizeGb = {};
|
||||
const newAvailGb = {};
|
||||
const newAvailableGb = {};
|
||||
const bytesPerGb = 1024 * 1024 * 1024;
|
||||
// Start from line 1 (skip header)
|
||||
for (var i = 1; i < lines.length; i++) {
|
||||
@@ -503,16 +462,19 @@ Singleton {
|
||||
const usedBytes = parseFloat(parts[2]) || 0;
|
||||
const sizeBytes = parseFloat(parts[3]) || 0;
|
||||
const availBytes = parseFloat(parts[4]) || 0;
|
||||
const availPercent = sizeBytes > 0 ? (availBytes / sizeBytes) * 100 : 0;
|
||||
newPercents[target] = percent;
|
||||
newAvailPercents[target] = Math.round(availPercent);
|
||||
newUsedGb[target] = usedBytes / bytesPerGb;
|
||||
newSizeGb[target] = sizeBytes / bytesPerGb;
|
||||
newAvailGb[target] = availBytes / bytesPerGb;
|
||||
newAvailableGb[target] = availBytes / bytesPerGb;
|
||||
}
|
||||
}
|
||||
root.diskPercents = newPercents;
|
||||
root.diskAvailPercents = newAvailPercents;
|
||||
root.diskUsedGb = newUsedGb;
|
||||
root.diskSizeGb = newSizeGb;
|
||||
root.diskAvailGb = newAvailGb;
|
||||
root.diskAvailableGb = newAvailableGb;
|
||||
root.pushDiskHistory();
|
||||
}
|
||||
}
|
||||
@@ -571,7 +533,6 @@ Singleton {
|
||||
if (!isNaN(maxKHz) && maxKHz > 0) {
|
||||
let newMaxFreq = maxKHz / 1000000.0;
|
||||
if (Math.abs(root.cpuGlobalMaxFreq - newMaxFreq) > 0.01) {
|
||||
Logger.i("SystemStat", `CPU Max Freq changed: ${root.cpuGlobalMaxFreq} -> ${newMaxFreq} GHz`);
|
||||
root.cpuGlobalMaxFreq = newMaxFreq;
|
||||
}
|
||||
}
|
||||
@@ -1006,27 +967,6 @@ Singleton {
|
||||
|
||||
root.rxSpeed = Math.round(rxDiff / timeDiff); // Speed in Bytes/s
|
||||
root.txSpeed = Math.round(txDiff / timeDiff);
|
||||
|
||||
// Record new peaks if higher than current max (for adaptive ratio calculation)
|
||||
const now = Date.now();
|
||||
if (root.rxSpeed > root.rxMaxSpeed) {
|
||||
networkStatsAdapter.rxPeaks = [...(networkStatsAdapter.rxPeaks || []),
|
||||
{
|
||||
speed: root.rxSpeed,
|
||||
timestamp: now
|
||||
}
|
||||
];
|
||||
networkStatsSaveDebounce.restart();
|
||||
}
|
||||
if (root.txSpeed > root.txMaxSpeed) {
|
||||
networkStatsAdapter.txPeaks = [...(networkStatsAdapter.txPeaks || []),
|
||||
{
|
||||
speed: root.txSpeed,
|
||||
timestamp: now
|
||||
}
|
||||
];
|
||||
networkStatsSaveDebounce.restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1081,7 +1021,7 @@ Singleton {
|
||||
// -------------------------------------------------------
|
||||
// Smart formatter for memory values (GB) - max 4 chars
|
||||
// Uses decimal for < 10GB, integer otherwise
|
||||
function formatMemoryGb(memGb) {
|
||||
function formatGigabytes(memGb) {
|
||||
const value = parseFloat(memGb);
|
||||
if (isNaN(value))
|
||||
return "0G";
|
||||
@@ -1091,6 +1031,22 @@ Singleton {
|
||||
return Math.round(value) + "G"; // "10G" to "999G"
|
||||
}
|
||||
|
||||
// -------------------------------------------------------
|
||||
// Formatting disk usage
|
||||
function formatDiskDisplay(diskPath, {
|
||||
percent = false,
|
||||
available = false
|
||||
} = {}) {
|
||||
if (percent) {
|
||||
const raw = available ? root.diskAvailPercents[diskPath] : root.diskPercents[diskPath];
|
||||
const value = (raw === null) ? 0 : raw;
|
||||
return `${value}%`;
|
||||
} else {
|
||||
const rawGb = available ? root.diskAvailableGb[diskPath] : root.diskUsedGb[diskPath];
|
||||
return formatGigabytes(rawGb === null ? 0 : rawGb);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------
|
||||
// Function to start fetching and computing the cpu temperature
|
||||
function updateCpuTemperature() {
|
||||
|
||||
@@ -51,6 +51,10 @@ Singleton {
|
||||
"key": "faithful",
|
||||
"name": I18n.tr("common.faithful")
|
||||
},
|
||||
{
|
||||
"key": "dysfunctional",
|
||||
"name": I18n.tr("common.dysfunctional")
|
||||
},
|
||||
{
|
||||
"key": "muted",
|
||||
"name": I18n.tr("common.color-muted")
|
||||
@@ -102,7 +106,7 @@ Singleton {
|
||||
|
||||
const script = buildGenerationScript(content, wp, mode);
|
||||
|
||||
generateProcess.command = ["sh", "-lc", script];
|
||||
generateProcess.command = ["sh", "-c", script];
|
||||
generateProcess.running = true;
|
||||
}
|
||||
|
||||
@@ -162,7 +166,7 @@ Singleton {
|
||||
// Add user templates if enabled
|
||||
script += buildUserTemplateCommandForPredefined(schemeData, mode);
|
||||
|
||||
generateProcess.command = ["sh", "-lc", script];
|
||||
generateProcess.command = ["sh", "-c", script];
|
||||
generateProcess.running = true;
|
||||
}
|
||||
|
||||
@@ -245,11 +249,14 @@ Singleton {
|
||||
if (isTemplateEnabled("code")) {
|
||||
app.clients.forEach(client => {
|
||||
// Check if this specific client is detected
|
||||
if (isCodeClientEnabled(client.name)) {
|
||||
lines.push(`\n[templates.code_${client.name}]`);
|
||||
lines.push(`input_path = "${Quickshell.shellDir}/Assets/Templates/${app.input}"`);
|
||||
const expandedPath = client.path.replace("~", homeDir);
|
||||
lines.push(`output_path = "${expandedPath}"`);
|
||||
var resolvedPaths = TemplateRegistry.resolvedCodeClientPaths(client.name);
|
||||
if (isCodeClientEnabled(client.name) && resolvedPaths.length > 0) {
|
||||
resolvedPaths.forEach((resolvedPath, pathIndex) => {
|
||||
var suffix = resolvedPaths.length > 1 ? `_${pathIndex}` : "";
|
||||
lines.push(`\n[templates.code_${client.name}${suffix}]`);
|
||||
lines.push(`input_path = "${Quickshell.shellDir}/Assets/Templates/${app.input}"`);
|
||||
lines.push(`output_path = "${resolvedPath}"`);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -399,7 +406,7 @@ Singleton {
|
||||
}
|
||||
});
|
||||
|
||||
copyProcess.command = ["sh", "-lc", script];
|
||||
copyProcess.command = ["sh", "-c", script];
|
||||
copyProcess.running = true;
|
||||
}
|
||||
|
||||
@@ -428,7 +435,7 @@ Singleton {
|
||||
});
|
||||
|
||||
if (commands.length > 0) {
|
||||
copyProcess.command = ["sh", "-lc", commands.join('; ')];
|
||||
copyProcess.command = ["sh", "-c", commands.join('; ')];
|
||||
copyProcess.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,11 @@ Singleton {
|
||||
|
||||
readonly property string templateApplyScript: Quickshell.shellDir + '/Scripts/bash/template-apply.sh'
|
||||
readonly property string gtkRefreshScript: Quickshell.shellDir + '/Scripts/python/src/theming/gtk-refresh.py'
|
||||
readonly property string vscodeHelperScript: Quickshell.shellDir + '/Scripts/python/src/theming/vscode-helper.py'
|
||||
|
||||
// Dynamically resolved VSCode extension theme paths (all matching noctalia extensions)
|
||||
property var resolvedCodePaths: []
|
||||
property var resolvedCodiumPaths: []
|
||||
|
||||
// Terminal configurations (for wallpaper-based templates)
|
||||
// Each terminal must define a postHook that sets up config includes and triggers reload
|
||||
@@ -398,6 +403,15 @@ Singleton {
|
||||
return clients;
|
||||
}
|
||||
|
||||
// Get resolved theme paths for a code client (returns array of all matching paths)
|
||||
function resolvedCodeClientPaths(clientName) {
|
||||
if (clientName === "code")
|
||||
return resolvedCodePaths;
|
||||
if (clientName === "codium")
|
||||
return resolvedCodiumPaths;
|
||||
return [];
|
||||
}
|
||||
|
||||
// Extract Code clients for ProgramCheckerService compatibility
|
||||
readonly property var codeClients: {
|
||||
var clients = [];
|
||||
@@ -417,13 +431,47 @@ Singleton {
|
||||
clients.push({
|
||||
"name": client.name,
|
||||
"configPath": baseConfigDir,
|
||||
"themePath": themePath
|
||||
"themePath": "" // resolved dynamically via resolvedCodeClientPaths()
|
||||
});
|
||||
});
|
||||
}
|
||||
return clients;
|
||||
}
|
||||
|
||||
// Resolve VSCode extension paths dynamically
|
||||
Process {
|
||||
id: codeResolverProcess
|
||||
command: ["python3", vscodeHelperScript, "~/.vscode/extensions"]
|
||||
running: true
|
||||
property var paths: []
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
var line = data.trim();
|
||||
if (line)
|
||||
codeResolverProcess.paths.push(line);
|
||||
}
|
||||
}
|
||||
onExited: {
|
||||
root.resolvedCodePaths = paths;
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: codiumResolverProcess
|
||||
command: ["python3", vscodeHelperScript, "~/.vscode-oss/extensions"]
|
||||
running: true
|
||||
property var paths: []
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
var line = data.trim();
|
||||
if (line)
|
||||
codiumResolverProcess.paths.push(line);
|
||||
}
|
||||
}
|
||||
onExited: {
|
||||
root.resolvedCodiumPaths = paths;
|
||||
}
|
||||
}
|
||||
// Build user templates TOML content
|
||||
function buildUserTemplatesToml() {
|
||||
var lines = [];
|
||||
|
||||
@@ -208,7 +208,8 @@ Singleton {
|
||||
"showSwapUsage": false,
|
||||
"showNetworkStats": false,
|
||||
"showDiskUsage": false,
|
||||
"showDiskAsFree": false,
|
||||
"showDiskUsageAsPercent": false,
|
||||
"showDiskAvailable": false,
|
||||
"diskPath": "/"
|
||||
},
|
||||
"Taskbar": {
|
||||
@@ -255,7 +256,8 @@ Singleton {
|
||||
"occupiedColor": "secondary",
|
||||
"emptyColor": "secondary",
|
||||
"showBadge": true,
|
||||
"reverseScroll": false
|
||||
"reverseScroll": false,
|
||||
"pillSize": 0.6
|
||||
},
|
||||
"Volume": {
|
||||
"displayMode": "onhover",
|
||||
|
||||
@@ -17,7 +17,7 @@ Item {
|
||||
// Reactive path that updates when values change
|
||||
readonly property string svgPath: {
|
||||
if (!values || !Array.isArray(values) || values.length === 0) {
|
||||
return "";
|
||||
return "M 0 0"; // Valid no-op path to avoid Qt triangulator crash on empty path
|
||||
}
|
||||
|
||||
// Apply minimum signal if enabled
|
||||
@@ -28,7 +28,7 @@ Item {
|
||||
const mirroredValues = partToMirror.concat(processedValues);
|
||||
|
||||
if (mirroredValues.length < 2) {
|
||||
return "";
|
||||
return "M 0 0"; // Valid no-op path to avoid Qt triangulator crash on empty path
|
||||
}
|
||||
|
||||
const count = mirroredValues.length;
|
||||
|
||||
+182
-138
@@ -1,10 +1,9 @@
|
||||
import QtQuick
|
||||
import QtQuick.Shapes
|
||||
import qs.Commons
|
||||
|
||||
Item {
|
||||
id: root
|
||||
clip: true // Clip bezier overshoot
|
||||
clip: true
|
||||
|
||||
// Primary line
|
||||
property var values: []
|
||||
@@ -27,12 +26,91 @@ Item {
|
||||
property bool fill: true
|
||||
property real fillOpacity: 0.15
|
||||
|
||||
// Smooth scrolling interval (how often data updates)
|
||||
property int updateInterval: 1000
|
||||
|
||||
// Auto-scale: when false, use minValue/maxValue directly (e.g., for 0-100% graphs)
|
||||
property bool autoScale: true
|
||||
property bool autoScale2: true
|
||||
|
||||
// Padding for bezier overshoot (percentage of range)
|
||||
readonly property real curvePadding: 0.08
|
||||
|
||||
readonly property bool hasData: values.length >= 2
|
||||
readonly property bool hasData2: values2.length >= 2
|
||||
|
||||
// Animation state
|
||||
property real _t: 1.0
|
||||
property bool _ready: false
|
||||
property real _pred: 0
|
||||
property real _pred2: 0
|
||||
|
||||
onValuesChanged: {
|
||||
if (values.length < 2)
|
||||
return;
|
||||
const last = values[values.length - 1];
|
||||
const prev = values.length > 1 ? values[values.length - 2] : last;
|
||||
_pred = Math.max(minValue, last + (last - prev));
|
||||
|
||||
if (!_ready) {
|
||||
_ready = true;
|
||||
_t = 0;
|
||||
_animTimer.start();
|
||||
return;
|
||||
}
|
||||
// Maintain continuity: new_t = old_t - 1
|
||||
_t = _t - 1.0;
|
||||
_animTimer.start();
|
||||
}
|
||||
|
||||
onValues2Changed: {
|
||||
if (values2.length < 2)
|
||||
return;
|
||||
const last = values2[values2.length - 1];
|
||||
const prev = values2.length > 1 ? values2[values2.length - 2] : last;
|
||||
_pred2 = Math.max(minValue2, last + (last - prev));
|
||||
// Let animation timer handle repaints - don't trigger here
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: _animTimer
|
||||
interval: 16
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
root._t = Math.min(1.0, root._t + (16 / root.updateInterval));
|
||||
canvas.requestPaint();
|
||||
if (root._t >= 1.0)
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
// Effective max values that include current data and predictions (when autoScale is true)
|
||||
readonly property real _effectiveMax: {
|
||||
if (!autoScale || !hasData)
|
||||
return maxValue;
|
||||
let m = maxValue;
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
if (values[i] > m)
|
||||
m = values[i];
|
||||
}
|
||||
if (_pred > m)
|
||||
m = _pred;
|
||||
return m;
|
||||
}
|
||||
|
||||
readonly property real _effectiveMax2: {
|
||||
if (!autoScale2 || !hasData2)
|
||||
return maxValue2;
|
||||
let m = maxValue2;
|
||||
for (let i = 0; i < values2.length; i++) {
|
||||
if (values2[i] > m)
|
||||
m = values2[i];
|
||||
}
|
||||
if (_pred2 > m)
|
||||
m = _pred2;
|
||||
return m;
|
||||
}
|
||||
|
||||
// Convert a value to Y coordinate (with padding for bezier curves)
|
||||
function valueToY(val, minVal, maxVal) {
|
||||
let range = maxVal - minVal;
|
||||
@@ -46,151 +124,117 @@ Item {
|
||||
return height - normalized * height;
|
||||
}
|
||||
|
||||
// Generate SVG path for a given values array using monotone cubic interpolation
|
||||
function generateCurvePath(vals, minVal, maxVal) {
|
||||
if (!vals || vals.length < 2 || width <= 0 || height <= 0)
|
||||
return "";
|
||||
|
||||
const n = vals.length;
|
||||
|
||||
// Build array of points
|
||||
let points = [];
|
||||
for (let i = 0; i < n; i++) {
|
||||
points.push({
|
||||
x: (i / (n - 1)) * width,
|
||||
y: valueToY(vals[i], minVal, maxVal)
|
||||
});
|
||||
}
|
||||
|
||||
// For only 2 points, draw a line
|
||||
if (points.length === 2) {
|
||||
return `M ${points[0].x.toFixed(2)} ${points[0].y.toFixed(2)} L ${points[1].x.toFixed(2)} ${points[1].y.toFixed(2)}`;
|
||||
}
|
||||
|
||||
// Calculate tangents using finite differences (monotone cubic)
|
||||
let tangents = [];
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
if (i === 0) {
|
||||
tangents.push((points[1].y - points[0].y) / (points[1].x - points[0].x));
|
||||
} else if (i === points.length - 1) {
|
||||
tangents.push((points[i].y - points[i - 1].y) / (points[i].x - points[i - 1].x));
|
||||
} else {
|
||||
// Average of left and right slopes
|
||||
const left = (points[i].y - points[i - 1].y) / (points[i].x - points[i - 1].x);
|
||||
const right = (points[i + 1].y - points[i].y) / (points[i + 1].x - points[i].x);
|
||||
tangents.push((left + right) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Build the path
|
||||
let path = `M ${points[0].x.toFixed(2)} ${points[0].y.toFixed(2)}`;
|
||||
|
||||
for (let i = 0; i < points.length - 1; i++) {
|
||||
const p0 = points[i];
|
||||
const p1 = points[i + 1];
|
||||
const dx = p1.x - p0.x;
|
||||
|
||||
// Control points for cubic bezier
|
||||
const cp1x = p0.x + dx / 3;
|
||||
const cp1y = p0.y + tangents[i] * dx / 3;
|
||||
const cp2x = p1.x - dx / 3;
|
||||
const cp2y = p1.y - tangents[i + 1] * dx / 3;
|
||||
|
||||
path += ` C ${cp1x.toFixed(2)},${cp1y.toFixed(2)} ${cp2x.toFixed(2)},${cp2y.toFixed(2)} ${p1.x.toFixed(2)},${p1.y.toFixed(2)}`;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
// Generate fill path (curve + bottom edge)
|
||||
function generateFillPath(curvePath) {
|
||||
if (!curvePath || width <= 0 || height <= 0)
|
||||
return "";
|
||||
return curvePath + ` L ${width.toFixed(2)} ${height.toFixed(2)} L 0 ${height.toFixed(2)} Z`;
|
||||
}
|
||||
|
||||
// Computed paths for primary line
|
||||
readonly property string curvePath: generateCurvePath(values, minValue, maxValue)
|
||||
readonly property string fillPath: generateFillPath(curvePath)
|
||||
|
||||
// Computed paths for secondary line
|
||||
readonly property string curvePath2: generateCurvePath(values2, minValue2, maxValue2)
|
||||
readonly property string fillPath2: generateFillPath(curvePath2)
|
||||
|
||||
Shape {
|
||||
Canvas {
|
||||
id: canvas
|
||||
anchors.fill: parent
|
||||
layer.enabled: true
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
visible: root.hasData || root.hasData2
|
||||
renderStrategy: Canvas.Cooperative
|
||||
|
||||
// Primary line fill
|
||||
ShapePath {
|
||||
strokeColor: "transparent"
|
||||
strokeWidth: 0
|
||||
fillGradient: LinearGradient {
|
||||
x1: 0
|
||||
y1: 0
|
||||
x2: 0
|
||||
y2: root.height
|
||||
GradientStop {
|
||||
position: 0.0
|
||||
color: Qt.rgba(root.color.r, root.color.g, root.color.b, root.fill ? root.fillOpacity : 0)
|
||||
}
|
||||
GradientStop {
|
||||
position: 1.0
|
||||
color: "transparent"
|
||||
}
|
||||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
if (width <= 0 || height <= 0)
|
||||
return;
|
||||
|
||||
// Use primary values length for consistent step size
|
||||
const baseLen = root.hasData ? root.values.length : (root.hasData2 ? root.values2.length : 0);
|
||||
if (baseLen < 2)
|
||||
return;
|
||||
const step = width / (baseLen - 1);
|
||||
|
||||
if (root.hasData) {
|
||||
drawGraph(ctx, root.values, root._pred, root.minValue, root._effectiveMax, root.color, root._t, step);
|
||||
}
|
||||
PathSvg {
|
||||
path: root.fillPath
|
||||
if (root.hasData2) {
|
||||
drawGraph(ctx, root.values2, root._pred2, root.minValue2, root._effectiveMax2, root.color2, root._t, step);
|
||||
}
|
||||
}
|
||||
|
||||
// Secondary line fill
|
||||
ShapePath {
|
||||
strokeColor: "transparent"
|
||||
strokeWidth: 0
|
||||
fillGradient: LinearGradient {
|
||||
x1: 0
|
||||
y1: 0
|
||||
x2: 0
|
||||
y2: root.height
|
||||
GradientStop {
|
||||
position: 0.0
|
||||
color: Qt.rgba(root.color2.r, root.color2.g, root.color2.b, root.fill && root.hasData2 ? root.fillOpacity : 0)
|
||||
}
|
||||
GradientStop {
|
||||
position: 1.0
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
||||
PathSvg {
|
||||
path: root.fillPath2
|
||||
}
|
||||
}
|
||||
function drawGraph(ctx, vals, pred, minVal, maxVal, lineColor, t, step) {
|
||||
if (!vals || vals.length < 2)
|
||||
return;
|
||||
|
||||
// Primary line stroke
|
||||
ShapePath {
|
||||
strokeColor: root.hasData ? root.color : "transparent"
|
||||
strokeWidth: root.strokeWidth
|
||||
fillColor: "transparent"
|
||||
joinStyle: ShapePath.RoundJoin
|
||||
capStyle: ShapePath.RoundCap
|
||||
PathSvg {
|
||||
path: root.curvePath
|
||||
}
|
||||
}
|
||||
const n = vals.length;
|
||||
|
||||
// Secondary line stroke
|
||||
ShapePath {
|
||||
strokeColor: root.hasData2 ? root.color2 : "transparent"
|
||||
strokeWidth: root.strokeWidth
|
||||
fillColor: "transparent"
|
||||
joinStyle: ShapePath.RoundJoin
|
||||
capStyle: ShapePath.RoundCap
|
||||
PathSvg {
|
||||
path: root.curvePath2
|
||||
// Build points with interpolated X positions for smooth scrolling
|
||||
let pts = [];
|
||||
for (let i = 0; i < n; i++) {
|
||||
const x = (i + 1 - t) * step;
|
||||
const y = root.valueToY(vals[i], minVal, maxVal);
|
||||
pts.push({
|
||||
x: x,
|
||||
y: y
|
||||
});
|
||||
}
|
||||
// Prediction point enters from right
|
||||
pts.push({
|
||||
x: (n + 1 - t) * step,
|
||||
y: root.valueToY(pred, minVal, maxVal)
|
||||
});
|
||||
|
||||
if (pts.length < 2)
|
||||
return;
|
||||
|
||||
// Calculate tangents for smooth bezier curves
|
||||
let tan = [];
|
||||
for (let i = 0; i < pts.length; i++) {
|
||||
let tg = 0;
|
||||
if (i === 0 && pts[1].x !== pts[0].x) {
|
||||
tg = (pts[1].y - pts[0].y) / (pts[1].x - pts[0].x);
|
||||
} else if (i === pts.length - 1 && pts[i].x !== pts[i - 1].x) {
|
||||
tg = (pts[i].y - pts[i - 1].y) / (pts[i].x - pts[i - 1].x);
|
||||
} else if (i > 0 && i < pts.length - 1) {
|
||||
const dxL = pts[i].x - pts[i - 1].x;
|
||||
const dxR = pts[i + 1].x - pts[i].x;
|
||||
const l = dxL !== 0 ? (pts[i].y - pts[i - 1].y) / dxL : 0;
|
||||
const r = dxR !== 0 ? (pts[i + 1].y - pts[i].y) / dxR : 0;
|
||||
tg = (l + r) / 2;
|
||||
}
|
||||
tan.push(tg);
|
||||
}
|
||||
|
||||
// Draw fill gradient
|
||||
if (root.fill) {
|
||||
let grad = ctx.createLinearGradient(0, 0, 0, height);
|
||||
grad.addColorStop(0, Qt.rgba(lineColor.r, lineColor.g, lineColor.b, root.fillOpacity));
|
||||
grad.addColorStop(1, "transparent");
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(pts[0].x, pts[0].y);
|
||||
for (let i = 0; i < pts.length - 1; i++) {
|
||||
const dx = pts[i + 1].x - pts[i].x;
|
||||
if (Math.abs(dx) < 0.1) {
|
||||
ctx.lineTo(pts[i + 1].x, pts[i + 1].y);
|
||||
continue;
|
||||
}
|
||||
const c1x = pts[i].x + dx / 3, c1y = pts[i].y + tan[i] * dx / 3;
|
||||
const c2x = pts[i + 1].x - dx / 3, c2y = pts[i + 1].y - tan[i + 1] * dx / 3;
|
||||
ctx.bezierCurveTo(c1x, c1y, c2x, c2y, pts[i + 1].x, pts[i + 1].y);
|
||||
}
|
||||
ctx.lineTo(pts[pts.length - 1].x, height);
|
||||
ctx.lineTo(pts[0].x, height);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = grad;
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
// Draw stroke
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(pts[0].x, pts[0].y);
|
||||
for (let i = 0; i < pts.length - 1; i++) {
|
||||
const dx = pts[i + 1].x - pts[i].x;
|
||||
if (Math.abs(dx) < 0.1) {
|
||||
ctx.lineTo(pts[i + 1].x, pts[i + 1].y);
|
||||
continue;
|
||||
}
|
||||
const c1x = pts[i].x + dx / 3, c1y = pts[i].y + tan[i] * dx / 3;
|
||||
const c2x = pts[i + 1].x - dx / 3, c2y = pts[i + 1].y - tan[i + 1] * dx / 3;
|
||||
ctx.bezierCurveTo(c1x, c1y, c2x, c2y, pts[i + 1].x, pts[i + 1].y);
|
||||
}
|
||||
ctx.strokeStyle = lineColor;
|
||||
ctx.lineWidth = root.strokeWidth;
|
||||
ctx.lineCap = "round";
|
||||
ctx.lineJoin = "round";
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@ Rectangle {
|
||||
Component.onCompleted: _applyDistribution()
|
||||
|
||||
function _updateFirstLast() {
|
||||
// Defensive check for QML initialization timing
|
||||
if (!tabRow || !tabRow.children) {
|
||||
return;
|
||||
}
|
||||
var kids = tabRow.children;
|
||||
var len = kids.length;
|
||||
var firstVisible = -1;
|
||||
@@ -43,6 +47,9 @@ Rectangle {
|
||||
}
|
||||
|
||||
function _applyDistribution() {
|
||||
if (!tabRow || !tabRow.children) {
|
||||
return;
|
||||
}
|
||||
if (!distributeEvenly) {
|
||||
for (var i = 0; i < tabRow.children.length; i++) {
|
||||
var child = tabRow.children[i];
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
imagemagick,
|
||||
wget,
|
||||
python3,
|
||||
wayland-scanner,
|
||||
# calendar support
|
||||
calendarSupport ? false,
|
||||
evolution-data-server,
|
||||
@@ -90,6 +91,7 @@ stdenvNoCC.mkDerivation {
|
||||
preFixup = ''
|
||||
qtWrapperArgs+=(
|
||||
--prefix PATH : ${lib.makeBinPath (runtimeDeps ++ extraPackages)}
|
||||
--prefix XDG_DATA_DIRS : ${wayland-scanner}/share
|
||||
--add-flags "-p $out/share/noctalia-shell"
|
||||
${lib.optionalString calendarSupport "--prefix GI_TYPELIB_PATH : ${giTypelibPath}"}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user