Merge branch 'main' into patch-2

This commit is contained in:
notiant
2025-12-22 02:42:27 +01:00
committed by GitHub
57 changed files with 1721 additions and 927 deletions
File diff suppressed because one or more lines are too long
+19 -10
View File
@@ -527,6 +527,7 @@
"close-app": "{app} schließen",
"connect-vpn": "Mit {name} verbinden",
"cycle-visualizer": "Zyklus-Visualisierer",
"delete": "Löschen",
"disable-bluetooth": "Bluetooth deaktivieren",
"disable-dnd": "'Nicht stören' deaktivieren",
"disable-wifi": "WLAN deaktivieren",
@@ -735,10 +736,6 @@
"top_left": "Oben links",
"top_right": "Oben rechts"
},
"visualizer-quality": {
"high": "Hoch",
"low": "Niedrig"
},
"visualizer-types": {
"linear": "Linear",
"mirrored": "Gespiegelt",
@@ -950,10 +947,6 @@
"description": "Bevorzugte und ignorierte Medienanwendungen festlegen.",
"label": "Medienplayer"
},
"visualizer-quality": {
"description": "Hohe Qualität verbraucht mehr GPU-Ressourcen.",
"label": "Visualisierungsqualität"
},
"visualizer-type": {
"description": "Visualisierungstyp für Medienwiedergabe wählen.",
"label": "Visualisierungstyp"
@@ -1411,16 +1404,18 @@
}
},
"edit-mode": {
"add-widget": "Widget hinzufügen",
"button": {
"label": "Bearbeitungsmodus aktivieren"
},
"controls-explanation": "Linke Maustaste: Widget verschieben\nRechte Maustaste: Widget-Größe ändern",
"controls-explanation": "Linksklick & Ziehen: Widget verschieben oder Größe ändern.\nRechtsklick: Kontextmenü-Optionen öffnen.",
"description": "Aktiviere den Bearbeitungsmodus, um Desktop-Widgets zu verschieben und neu zu positionieren. Im aktivierten Zustand zeigen Widgets eine Ziehumrandung und können neu positioniert werden.",
"exit-button": "Bearbeitungsmodus verlassen",
"grid-snap": {
"label": "Raster einrasten"
},
"label": "Bearbeitungsmodus"
"label": "Bearbeitungsmodus",
"open-settings": "Einstellungen öffnen"
},
"enabled": {
"description": "Desktop-Widgets vollständig aktivieren oder deaktivieren.",
@@ -1445,6 +1440,10 @@
"description": "Zeige den Hintergrundcontainer für das Media-Player-Widget an.",
"label": "Hintergrund anzeigen"
},
"show-buttons": {
"description": "Mediensteuerungstasten (Wiedergabe/Pause, Zurück, Weiter) im Media-Player-Widget anzeigen.",
"label": "Tasten anzeigen"
},
"visualizer-type": {
"description": "Wählen Sie einen Visualisierungstyp für den Hintergrund des Desktop-Mediaplayers.",
"label": "Visualisierungsart"
@@ -1718,6 +1717,10 @@
"placeholder": "z.B. notify-send \"Hintergrundbild\" \"Geändert\""
}
},
"indicator": {
"default-value": "Standard: {value}",
"system-default": "Systemstandard"
},
"launcher": {
"settings": {
"clip-preview": {
@@ -1740,6 +1743,10 @@
"description": "Elemente in einem Raster statt in einer Liste anzeigen.",
"label": "Rasteransicht"
},
"icon-mode": {
"description": "Verwende native System-Icons anstelle von Tabler-Icons.",
"label": "Native Icons verwenden"
},
"position": {
"description": "Wählen Sie, wo das Starter-Panel erscheint.",
"label": "Position"
@@ -1865,6 +1872,7 @@
},
"network": {
"bluetooth": {
"description": "Bluetooth-Verwaltung aktivieren.",
"label": "Bluetooth aktivieren"
},
"section": {
@@ -1872,6 +1880,7 @@
},
"title": "Netzwerk",
"wifi": {
"description": "Drahtlosnetzwerke verwalten (erfordert NetworkManager).",
"label": "WLAN aktivieren"
}
},
+19 -14
View File
@@ -527,6 +527,7 @@
"close-app": "Close {app}",
"connect-vpn": "Connect to {name}",
"cycle-visualizer": "Cycle visualizer",
"delete": "Delete",
"disable-bluetooth": "Disable Bluetooth",
"disable-dnd": "Disable Do Not Disturb",
"disable-wifi": "Disable Wi-Fi",
@@ -735,10 +736,6 @@
"top_left": "Top left",
"top_right": "Top right"
},
"visualizer-quality": {
"high": "High",
"low": "Low"
},
"visualizer-types": {
"linear": "Linear",
"mirrored": "Mirrored",
@@ -882,10 +879,6 @@
"title": "Session Menu"
},
"settings": {
"indicator": {
"default-value": "Default: {value}",
"system-default": "System Default"
},
"about": {
"contributors": {
"section": {
@@ -954,10 +947,6 @@
"description": "Set your preferred and ignored media applications.",
"label": "Media players"
},
"visualizer-quality": {
"description": "High quality use more GPU ressources.",
"label": "Visualization quality"
},
"visualizer-type": {
"description": "Choose a visualization type for media playback.",
"label": "Visualization type"
@@ -1415,16 +1404,18 @@
}
},
"edit-mode": {
"add-widget": "Add widget",
"button": {
"label": "Enter edit mode"
},
"controls-explanation": "Left mouse button: Move widget\nRight mouse button: Resize widget",
"controls-explanation": "Left-click & drag: Move or resize the widget.\nRight-click: Open the context menu options.",
"description": "Enable edit mode to move and reposition desktop widgets. When enabled, widgets show a drag outline and can be repositioned.",
"exit-button": "Exit edit mode",
"grid-snap": {
"label": "Grid snap"
},
"label": "Edit mode"
"label": "Edit mode",
"open-settings": "Open settings"
},
"enabled": {
"description": "Enable or disable desktop widgets entirely.",
@@ -1449,6 +1440,10 @@
"description": "Show the background container for the media player widget.",
"label": "Show background"
},
"show-buttons": {
"description": "Show media control buttons (play/pause, previous, next) on the media player widget.",
"label": "Show buttons"
},
"visualizer-type": {
"description": "Choose a visualization type for the desktop media player background.",
"label": "Visualization type"
@@ -1722,6 +1717,10 @@
"placeholder": "e.g., notify-send \"Wallpaper\" \"Changed\""
}
},
"indicator": {
"default-value": "Default: {value}",
"system-default": "System Default"
},
"launcher": {
"settings": {
"clip-preview": {
@@ -1744,6 +1743,10 @@
"description": "Display items in a grid layout instead of a list.",
"label": "Grid view"
},
"icon-mode": {
"description": "Use native system icons instead of Tabler icons.",
"label": "Use native icons"
},
"position": {
"description": "Choose where the launcher panel appears.",
"label": "Position"
@@ -1869,6 +1872,7 @@
},
"network": {
"bluetooth": {
"description": "Activate Bluetooth management.",
"label": "Enable Bluetooth"
},
"section": {
@@ -1876,6 +1880,7 @@
},
"title": "Network",
"wifi": {
"description": "Manage wireless networks (requires NetworkManager).",
"label": "Enable Wi-Fi"
}
},
+19 -10
View File
@@ -527,6 +527,7 @@
"close-app": "Cerrar {app}",
"connect-vpn": "Conectarse a {name}",
"cycle-visualizer": "Visualizador de ciclos",
"delete": "Borrar",
"disable-bluetooth": "Desactivar Bluetooth",
"disable-dnd": "Desactivar No molestar",
"disable-wifi": "Desactivar Wi-Fi",
@@ -735,10 +736,6 @@
"top_left": "Superior izquierda",
"top_right": "Superior derecha"
},
"visualizer-quality": {
"high": "Alto/Alta",
"low": "Bajo"
},
"visualizer-types": {
"linear": "Lineal",
"mirrored": "Espejado",
@@ -950,10 +947,6 @@
"description": "Establece tus aplicaciones multimedia preferidas e ignoradas.",
"label": "Reproductores multimedia"
},
"visualizer-quality": {
"description": "Alta calidad usa más recursos de la GPU.",
"label": "Calidad de visualización"
},
"visualizer-type": {
"description": "Elige un tipo de visualización para la reproducción de medios.",
"label": "Tipo de visualización"
@@ -1411,16 +1404,18 @@
}
},
"edit-mode": {
"add-widget": "Añadir widget",
"button": {
"label": "Entrar en modo de edición"
},
"controls-explanation": "Botón izquierdo del ratón: Mover widget\nBotón derecho del ratón: Redimensionar widget",
"controls-explanation": "Clic izquierdo y arrastrar: Mover o redimensionar el widget.\nClic derecho: Abrir las opciones del menú contextual.",
"description": "Habilita el modo de edición para mover y cambiar la posición de los widgets del escritorio. Cuando está habilitado, los widgets muestran un contorno de arrastre y se pueden reposicionar.",
"exit-button": "Salir del modo de edición",
"grid-snap": {
"label": "Ajustar a cuadrícula"
},
"label": "Modo de edición"
"label": "Modo de edición",
"open-settings": "Abrir ajustes"
},
"enabled": {
"description": "Activar o desactivar los widgets de escritorio por completo.",
@@ -1445,6 +1440,10 @@
"description": "Mostrar el contenedor de fondo para el widget del reproductor multimedia.",
"label": "Mostrar fondo"
},
"show-buttons": {
"description": "Mostrar botones de control de medios (reproducir/pausar, anterior, siguiente) en el widget del reproductor multimedia.",
"label": "Mostrar botones"
},
"visualizer-type": {
"description": "Elige un tipo de visualización para el fondo del reproductor multimedia de escritorio.",
"label": "Tipo de visualización"
@@ -1718,6 +1717,10 @@
"placeholder": "ej., notify-send \"Fondo de pantalla\" \"Cambiado\""
}
},
"indicator": {
"default-value": "Predeterminado: {value}",
"system-default": "Predeterminado del sistema"
},
"launcher": {
"settings": {
"clip-preview": {
@@ -1740,6 +1743,10 @@
"description": "Mostrar elementos en una cuadrícula en lugar de una lista.",
"label": "Vista de cuadrícula"
},
"icon-mode": {
"description": "Usa iconos nativos del sistema en lugar de iconos Tabler.",
"label": "Usar iconos nativos"
},
"position": {
"description": "Elige dónde aparece el panel del lanzador.",
"label": "Posición"
@@ -1865,6 +1872,7 @@
},
"network": {
"bluetooth": {
"description": "Activar la gestión de Bluetooth.",
"label": "Activar Bluetooth"
},
"section": {
@@ -1872,6 +1880,7 @@
},
"title": "Red",
"wifi": {
"description": "Administrar redes inalámbricas (requiere NetworkManager).",
"label": "Activar Wi-Fi"
}
},
+19 -10
View File
@@ -527,6 +527,7 @@
"close-app": "Fermer {app}",
"connect-vpn": "Se connecter à {name}",
"cycle-visualizer": "Visualiseur de cycle",
"delete": "Supprimer",
"disable-bluetooth": "Désactiver le Bluetooth",
"disable-dnd": "Désactiver le mode Ne pas déranger",
"disable-wifi": "Désactiver le Wi-Fi",
@@ -735,10 +736,6 @@
"top_left": "En haut à gauche",
"top_right": "En haut à droite"
},
"visualizer-quality": {
"high": "Haut/Haute",
"low": "Bas"
},
"visualizer-types": {
"linear": "Linéaire",
"mirrored": "Miroir",
@@ -950,10 +947,6 @@
"description": "Définissez vos applications multimédias préférées et ignorées.",
"label": "Lecteurs multimédias"
},
"visualizer-quality": {
"description": "L'utilisation de haute qualité nécessite plus de ressources GPU.",
"label": "Qualité de la visualisation"
},
"visualizer-type": {
"description": "Choisissez un type de visualisation pour la lecture multimédia.",
"label": "Type de visualisation"
@@ -1411,16 +1404,18 @@
}
},
"edit-mode": {
"add-widget": "Ajouter un widget",
"button": {
"label": "Entrer en mode édition"
},
"controls-explanation": "Bouton gauche de la souris : Déplacer le widget\nBouton droit de la souris : Redimensionner le widget",
"controls-explanation": "Clic gauche et glisser-déposer : Déplacer ou redimensionner le widget.\nClic droit : Ouvrir les options du menu contextuel.",
"description": "Activer le mode édition pour déplacer et repositionner les widgets du bureau. Une fois activé, les widgets affichent un contour de glissement et peuvent être repositionnés.",
"exit-button": "Quitter le mode édition",
"grid-snap": {
"label": "Aligner sur la grille"
},
"label": "Mode édition"
"label": "Mode édition",
"open-settings": "Ouvrir les paramètres"
},
"enabled": {
"description": "Activer ou désactiver complètement les widgets du bureau.",
@@ -1445,6 +1440,10 @@
"description": "Afficher le conteneur d'arrière-plan pour le widget lecteur multimédia.",
"label": "Afficher l'arrière-plan"
},
"show-buttons": {
"description": "Afficher les boutons de contrôle multimédia (lecture/pause, précédent, suivant) sur le widget lecteur multimédia.",
"label": "Afficher les boutons"
},
"visualizer-type": {
"description": "Choisissez un type de visualisation pour l'arrière-plan du lecteur multimédia de bureau.",
"label": "Type de visualisation"
@@ -1718,6 +1717,10 @@
"placeholder": "ex: notify-send \"Fond d'écran\" \"Changé\""
}
},
"indicator": {
"default-value": "Par défaut : {value}",
"system-default": "Par défaut du système"
},
"launcher": {
"settings": {
"clip-preview": {
@@ -1740,6 +1743,10 @@
"description": "Afficher les éléments dans une grille au lieu d'une liste.",
"label": "Vue grille"
},
"icon-mode": {
"description": "Utiliser les icônes natives du système au lieu des icônes Tabler.",
"label": "Utiliser les icônes natives"
},
"position": {
"description": "Choisissez où le panneau du lanceur apparaît.",
"label": "Position"
@@ -1865,6 +1872,7 @@
},
"network": {
"bluetooth": {
"description": "Activer la gestion Bluetooth.",
"label": "Activer le Bluetooth"
},
"section": {
@@ -1872,6 +1880,7 @@
},
"title": "Réseau",
"wifi": {
"description": "Gérer les réseaux sans fil (nécessite NetworkManager).",
"label": "Activer le Wi-Fi"
}
},
+220 -211
View File
@@ -47,9 +47,9 @@
},
"battery": {
"device": {
"default": "標準 (表示デバイス)",
"description": "表示するバッテリーデバイスを選択してください。",
"label": "電池デバイス"
"default": "デフォルト(表示デバイス",
"description": "表示するバッテリーデバイスを選択します。",
"label": "バッテリーデバイス"
},
"display-mode": {
"description": "値の表示方法を選択します。",
@@ -60,11 +60,11 @@
"label": "低バッテリー警告の閾値"
},
"show-noctalia-performance": {
"description": "バッテリーパネルにNoctaliaパフォーマンスモードのトグルを表示します。",
"label": "Noctaliaパフォーマンストグルを表示"
"description": "バッテリーパネルに Noctalia パフォーマンスモードのトグルを表示します。",
"label": "Noctalia パフォーマンストグルを表示"
},
"show-power-profile": {
"description": "バッテリーパネルに電源プロファイルの選択を表示します。",
"description": "バッテリーパネルに電源プロファイルの選択項目を表示します。",
"label": "電源プロファイルコントロールを表示"
}
},
@@ -111,12 +111,12 @@
"browse-file": "ファイルを参照",
"browse-library": "ライブラリを参照",
"color-selection": {
"description": "アイコンにテーマの色を適用します。",
"description": "アイコンに適用するテーマの色を選択します。",
"label": "色の選択"
},
"enable-colorization": {
"description": "コントロールセンターのアイコンの色付けを有効にし、テーマの色を適用します。",
"label": "色の有効化"
"description": "コントロールセンターのアイコンにテーマカラーを適用します。",
"label": "テーマカラーの適用"
},
"icon": {
"description": "ライブラリまたはカスタムファイルからアイコンを選択します。",
@@ -142,10 +142,10 @@
"dynamic-text": "動的テキスト",
"hide-mode": {
"alwaysExpanded": "常に展開",
"description": "コマンド出力を持っていない場合のウィジェットの表示を制御します。",
"expandWithOutput": "出力があるときに展開",
"description": "コマンド出力ない場合のウィジェットの表示を制御します。",
"expandWithOutput": "出力がある時のみ展開",
"label": "非表示モード",
"maxTransparent": "最大展開だが透過"
"maxTransparent": "最大展開(透明)"
},
"icon": {
"description": "ライブラリからアイコンを選択します。",
@@ -312,8 +312,8 @@
"label": "ディスクパス"
},
"gpu-temperature": {
"description": "利用可能であれば、GPU温度の測定値を表示します。",
"label": "GPU温度 (GPU ondo)"
"description": "利用可能な場合、GPU 温度を表示します。",
"label": "GPU 温度"
},
"memory-percentage": {
"description": "メモリ使用量を絶対値ではなくパーセンテージで表示します。",
@@ -346,7 +346,7 @@
"label": "アイコンのスケーリング"
},
"max-width": {
"description": "画面幅に対するタスクバーの最大幅の割合。",
"description": "タスクバーの最大幅を、画面幅に対するパーセンテージで設定します。",
"label": "最大幅"
},
"only-active-workspaces": {
@@ -354,26 +354,26 @@
"label": "アクティブなワークスペースのみ"
},
"only-same-monitor": {
"description": "バーがあるモニターのアプリのみ表示す。",
"label": "同じモニターからのみ"
"description": "バーが配置されているディスプレイ上のアプリのみ表示します。",
"label": "同じディスプレイのみ"
},
"show-pinned-apps": {
"description": "ドックからピン留めされたアプリをタスクバーに表示す。",
"label": "ピン留めされたアプリを表示"
"description": "ドックピン留めされたアプリをタスクバーに表示します。",
"label": "ピン留めアプリを表示"
},
"show-title": {
"description": "タスクバーにウィンドウタイトルを表示します。",
"description-disabled": "縦型タスクバーはタイトル表示に対応していません。",
"description-disabled": "垂直タスクバーはタイトル表示できません。",
"label": "タイトルを表示"
},
"smart-width": {
"description": "エントリの数に基づいてエントリの幅を自動的に調整します。",
"label": "スマート幅"
"description": "項目の数に基づいて、項目の幅を自動的に調整します。",
"label": "幅の自動調整"
},
"title-width": {
"description": "タスクバーのウィンドウタイトルの幅を設定します(ピクセル単位)。",
"label": "タイトル幅",
"reset-tooltip": "タイトル幅をリセット"
"label": "タイトル幅",
"reset-tooltip": "タイトル幅をリセット"
}
},
"tray": {
@@ -386,8 +386,8 @@
"label": "ドロワーを有効化"
},
"hide-passive": {
"description": "有効にすると、非アクティブなステータスのトレイアイコンは非表示になります。",
"label": "受動的なアイテムを隠す"
"description": "有効にすると、ステータスが「パッシブ」のトレイ項目を非表示にます。",
"label": "パッシブ項目を非表示"
}
},
"volume": {
@@ -402,8 +402,8 @@
"label": "文字数"
},
"enable-scrollwheel": {
"description": "マウスホイールワークスペースを切り替え。",
"label": "ワークスペースを切り替えるにはスクロールしてください"
"description": "マウスホイールを使用してワークスペースを切り替えます。",
"label": "スクロールでワークスペースを切り替え"
},
"follow-focused-screen": {
"description": "バーが配置されている画面ではなく、現在フォーカスされている画面のワークスペースを表示します。",
@@ -418,12 +418,12 @@
"label": "ラベルモード"
},
"show-applications": {
"description": "ワークスペース内にアプリケーションのアイコンを表示す。",
"label": "アプリケーションを表示 (Apuriikeeshon o hyōji)"
"description": "ワークスペース内にアプリアイコンを表示します。",
"label": "アプリアイコンを表示"
},
"show-labels-only-when-occupied": {
"description": "ウィンドウを含む場合にのみ、ワークスペースラベルを表示す。",
"label": "占有されている場合のみラベルを表示する"
"description": "ウィンドウが開いているワークスペースにのみラベルを表示します。",
"label": "ウィンドウがある時のみラベルを表示"
}
}
}
@@ -474,7 +474,7 @@
"minutes": "分",
"pause": "一時停止",
"reset": "リセット",
"resume": "履歴書 (Rirekisho)",
"resume": "再開",
"seconds": "秒",
"start": "開始",
"stopwatch": "ストップウォッチ",
@@ -527,6 +527,7 @@
"close-app": "{app} を閉じる",
"connect-vpn": "{name} に接続",
"cycle-visualizer": "ビジュアライザーを切り替える",
"delete": "削除 (Sakujo)",
"disable-bluetooth": "Bluetooth を無効化",
"disable-dnd": "おやすみモードを無効化",
"disable-wifi": "Wi-Fi を無効化",
@@ -563,7 +564,7 @@
"launcher": {
"categories": {
"all": "すべて",
"audiovideo": "音声 & ビデオ",
"audiovideo": "音声ビデオ",
"chat": "チャット",
"development": "開発",
"education": "教育",
@@ -576,7 +577,7 @@
"system": "システム",
"webbrowser": "ウェブブラウザ"
},
"delete": "削除 (Sakujo)",
"delete": "削除",
"pin": "ピン留め",
"unpin": "ピン留め解除"
},
@@ -598,7 +599,7 @@
},
"range": {
"all": "すべて",
"earlier": "以前",
"earlier": "それ以前",
"today": "今日",
"yesterday": "昨日"
},
@@ -720,9 +721,9 @@
"never": "スクロールしない"
},
"settings-panel-mode": {
"attached": "棒に取り付けられたパネル",
"centered": "中央パネル",
"window": "ウィンドウ"
"attached": "バーに吸着",
"centered": "中央に配置",
"window": "独立したウィンドウ"
},
"shadow-direction": {
"bottom": "下",
@@ -735,10 +736,6 @@
"top_left": "左上",
"top_right": "右上"
},
"visualizer-quality": {
"high": "高",
"low": "低"
},
"visualizer-types": {
"linear": "リニア",
"mirrored": "ミラー",
@@ -783,7 +780,7 @@
"clipboard-clear-description": "クリップボード履歴をすべて消去",
"clipboard-clear-description-full": "クリップボード履歴からすべての項目を削除します",
"clipboard-clear-history": "クリップボード履歴を消去",
"clipboard-delete": "クリップボードのエントリを削除",
"clipboard-delete": "クリップボードの項目を削除",
"clipboard-history-disabled": "クリップボード履歴は無効です",
"clipboard-history-disabled-description": "設定で履歴を有効にするか、cliphist をインストールしてください",
"clipboard-loading": "クリップボード履歴を読み込み中...",
@@ -891,7 +888,7 @@
}
},
"noctalia": {
"git-commit": "Gitコミット:",
"git-commit": "Git コミット:",
"git-commit-loading": "読み込み中...",
"installed-version": "現在のバージョン:",
"latest-version": "最新のバージョン:",
@@ -950,10 +947,6 @@
"description": "優先するメディアアプリや、除外設定を行います。",
"label": "メディアプレーヤー"
},
"visualizer-quality": {
"description": "品質を高くすると、GPUリソースをより多く消費します。",
"label": "ビジュアライザーの品質"
},
"visualizer-type": {
"description": "メディア再生時の視覚効果(ビジュアライザー)の種類を選択します。",
"label": "ビジュアライザーの種類"
@@ -961,7 +954,7 @@
},
"panel": {
"applications": {
"empty": "現在音声を再生しているアプリケーションはありません"
"empty": "現在音声を再生中のアプリケーションはありません"
},
"input": "入力",
"output": "出力",
@@ -1039,12 +1032,12 @@
"label": "カプセルを表示"
},
"show-outline": {
"description": "すべてのウィジェットの周りに目に見える境界線を表示します。",
"label": "ウィジェットのアウトラインを表示"
"description": "すべてのウィジェットの周囲に枠線を表示します。",
"label": "ウィジェットの枠線を表示"
},
"transparent": {
"description": "これにより、パネルバーに取り付けられるのを防ぎます。",
"label": "透明な背景"
"description": "これを有効にすると、パネルバーに吸着されなくなります。",
"label": "背景の透明化"
}
},
"monitors": {
@@ -1066,7 +1059,7 @@
},
"widgets": {
"section": {
"description": "ウィジェットをドラッグして順番を変更します。右クリックメニューを使って、セクション間でウィジェットを移動したり、削除したりできます。",
"description": "ウィジェットをドラッグして並べ替えます。右クリックメニューから、セクション間の移動や削除が行えます。",
"label": "ウィジェットの配置"
}
}
@@ -1155,75 +1148,75 @@
},
"templates": {
"compositors": {
"description": "コンポジタのテーマ設定",
"label": "合成者",
"description": "コンポジタのテーマ設定",
"label": "コンポジタ",
"mango": {
"description": "{filepath} を書き込みます。mango が必要です",
"description-missing": "{app} インストールされている必要があります"
"description": "{filepath} を書き込みます。mango が必要です",
"description-missing": "{app} インストールが必要です。"
},
"niri": {
"description": "{filepath} を書き込みます。niri v25.11+ が必要です",
"description-missing": "{app} インストールされている必要があります"
"description": "{filepath} を書き込みます。niri v25.11+ が必要です",
"description-missing": "{app} インストールが必要です。"
}
},
"misc": {
"description": "独自のテンプレートを作成",
"label": "詳細設定",
"user-templates": {
"description": "操作方法を理解している場合のみ有効にしてくださいオンラインドキュメントを参照してください",
"description": "操作方法を理解している場合のみ有効にしてくださいオンラインドキュメントを参照してください",
"label": "ユーザーテンプレートを有効化"
}
},
"programs": {
"cava": {
"description": "{filepath} を書き込みます",
"description-missing": "{app} のインストールが必要です"
"description": "{filepath} を書き込みます",
"description-missing": "{app} のインストールが必要です"
},
"code": {
"description": "{filepath} を書き込みますHyprLuna テーマを手動でインストールして有効化する必要があります",
"description-missing": "Code クライアントが検出されません。VSCode または VSCodium をインストールしてください"
"description": "{filepath} を書き込みますHyprLuna テーマを手動でインストールして有効化する必要があります",
"description-missing": "Code クライアントが検出されません。VSCode または VSCodium をインストールしてください"
},
"description": "アプリケーションごとのテーマ設定",
"discord": {
"description": "{client} 用に {filepath} を書き込みますHyprLuna テーマを手動でインストールして有効化する必要があります",
"description-missing": "Discord クライアントが検出されませんvencord、vesktop、webcord、armcord、equibop、lightcord、dorion のいずれかをインストールしてください"
"description": "{client} 用に {filepath} を書き込みますHyprLuna テーマを手動でインストールして有効化する必要があります",
"description-missing": "Discord クライアントが検出されませんvencord、vesktop、webcord、armcord、equibop、lightcord、dorion のいずれかをインストールしてください"
},
"emacs": {
"description": "Doom: ~/.config/doom/themes/noctalia.el<br>標準: ~/.emacs.d/themes/noctalia.el<br>手動で適用: (load-theme 'noctalia t)",
"description-missing": "{app} インストールされている必要があります"
"description-missing": "{app} インストールが必要です。"
},
"fuzzel": {
"description": "{filepath} を書き込み、リロードします",
"description-missing": "{app} のインストールが必要です"
"description": "{filepath} を書き込み、リロードします",
"description-missing": "{app} のインストールが必要です"
},
"label": "プログラム",
"pywalfox": {
"description": "{filepath} を書き込み、pywalfox update を実行します",
"description-missing": "{app} のインストールが必要です"
"description": "{filepath} を書き込み、pywalfox update を実行します",
"description-missing": "{app} のインストールが必要です"
},
"spicetify": {
"description": "{filepath} を書き込みますComfy テーマを手動でインストールして有効化する必要があります",
"description-missing": "{app} のインストールが必要です"
"description": "{filepath} を書き込みますComfy テーマを手動でインストールして有効化する必要があります",
"description-missing": "{app} のインストールが必要です"
},
"telegram": {
"description": "{filepath} を書き込みます",
"description-missing": "{app} のインストールが必要です"
"description": "{filepath} を書き込みます",
"description-missing": "{app} のインストールが必要です"
},
"vicinae": {
"description": "{filepath} を書き込み、リロードします",
"description-missing": "{app} のインストールが必要です"
"description": "{filepath} を書き込み、リロードします",
"description-missing": "{app} のインストールが必要です"
},
"walker": {
"description": "{filepath} を書き込み、テーマを noctalia に設定します",
"description-missing": "{app} のインストールが必要です"
"description": "{filepath} を書き込み、テーマを noctalia に設定します",
"description-missing": "{app} のインストールが必要です"
},
"yazi": {
"description": "{filepath} を書き込み、flavor は手動で有効する必要があります",
"description-missing": "{app} インストールされている必要があります"
"description": "{filepath} を書き込みます。フレーバーは手動で有効する必要があります",
"description-missing": "{app} インストールが必要です。"
},
"zed": {
"description": "{filepath} を書き込み、リロードします",
"description-missing": "{app} のインストールが必要です"
"description": "{filepath} を書き込み、リロードします",
"description-missing": "{app} のインストールが必要です"
}
},
"section": {
@@ -1341,7 +1334,7 @@
"cancel": "キャンセル"
},
"section": {
"description": "ウィジェットをドラッグして順番を変更します。右クリックメニューを使って、セクション間でウィジェットを移動したり、削除したりできます。",
"description": "ウィジェットをドラッグして並べ替えます。右クリックメニューから、セクション間の移動や削除が行えます。",
"label": "ショートカットウィジェット"
},
"sectionLeft": "左",
@@ -1362,28 +1355,28 @@
"search-placeholder": "フォントを検索..."
},
"enabled": {
"description": "デスクトップに時計ウィジェットを表示す。",
"label": "時計ウィジェットを有効にする"
"description": "デスクトップに時計ウィジェットを表示します。",
"label": "時計ウィジェットを有効"
},
"format": {
"description": "ヒント: \\n を使用すると改行できます。",
"label": "フォーマット"
},
"height": {
"description": "時計ウィジェットの高さピクセル単位。",
"description": "時計ウィジェットの高さピクセル単位で設定します。",
"label": "高さ"
},
"minimal-mode": {
"description": "時刻と日付を含むミニマルな時計表示を使用します。",
"description": "時刻と日付のみのミニマルな時計表示を使用します。",
"label": "ミニマルモード"
},
"preview": "プレビュー",
"show-background": {
"description": "時計ウィジェットの背景コンテナを表示す。",
"description": "時計ウィジェットの背景コンテナを表示します。",
"label": "背景を表示"
},
"show-date": {
"description": "現在時刻の下に今日の日付を表示しなさい。",
"description": "時刻の下に現在の日付を表示します。",
"label": "日付を表示"
},
"show-seconds": {
@@ -1392,9 +1385,9 @@
},
"style": {
"analog": "アナログ",
"description": "時計の表示スタイルを選択してください。",
"description": "時計の表示スタイルを選択します。",
"digital": "デジタル",
"label": "時計スタイル",
"label": "時計スタイル",
"minimal": "ミニマル"
},
"use-custom-font": {
@@ -1402,29 +1395,31 @@
"label": "カスタムフォントを使用"
},
"use-primary-color": {
"description": "有効にすると、強調のためにプライマリカラーが適用されます。",
"description": "有効にすると、強調のためにプライマリカラーが適用ます。",
"label": "プライマリカラーを使用"
},
"width": {
"description": "クロックウィジェットの幅ピクセル単位。",
"description": "時計ウィジェットの幅ピクセル単位で設定します。",
"label": "幅"
}
},
"edit-mode": {
"add-widget": "ウィジェットを追加",
"button": {
"label": "編集モードに入る"
},
"controls-explanation": "左マウスボタン: ウィジェット移動\n右マウスボタン: ウィジェットのサイズを変更",
"description": "デスクトップウィジェット移動および再配置するには、編集モードを有効にしてください。有効にするとウィジェットにドラッグアウトラインが表示され、再配置できます。",
"exit-button": "編集モードを終了します。",
"controls-explanation": "左クリックでドラッグ:ウィジェット移動またはサイズ変更。\n右クリック:コンテキストメニューオプションを開く。",
"description": "編集モードを有効にして、デスクトップウィジェット移動や配置変更を行います。有効にするとウィジェットに枠線が表示され、位置を変更できるようになります。",
"exit-button": "編集モードを終了",
"grid-snap": {
"label": "グリッドスナップ"
"label": "グリッドスナップ"
},
"label": "編集モード"
"label": "編集モード",
"open-settings": "設定を開く"
},
"enabled": {
"description": "デスクトップウィジェットを完全に有効または無効にします。",
"label": "デスクトップウィジェットを有効にする"
"description": "デスクトップウィジェット全体を有効または無効にします。",
"label": "デスクトップウィジェットを有効"
},
"general": {
"section": {
@@ -1434,34 +1429,38 @@
},
"media-player": {
"enabled": {
"description": "デスクトップにメディアプレーヤーウィジェットを表示す。",
"label": "メディアプレーヤーウィジェットを有効にする"
"description": "デスクトップにメディアプレーヤーウィジェットを表示します。",
"label": "メディアプレーヤーウィジェットを有効"
},
"hide-mode": {
"description": "再生中のメディアがない場合のウィジェットの動作を制御します。",
"label": "非表示モード"
},
"show-background": {
"description": "メディアプレーヤーウィジェットの背景コンテナを表示す。",
"description": "メディアプレーヤーウィジェットの背景コンテナを表示します。",
"label": "背景を表示"
},
"show-buttons": {
"description": "メディアプレーヤーウィジェットにメディアコントロールボタン(再生/一時停止、前へ、次へ)を表示します。",
"label": "ボタンを表示"
},
"visualizer-type": {
"description": "デスクトップメディアプレーヤーの背景の視覚化タイプを選択してください。",
"label": "視覚化の種類"
"description": "デスクトップメディアプレーヤーの背景に使用するビジュアライザーの種類を選択します。",
"label": "ビジュアライザーの種類"
},
"visualizer-visibility": {
"description": "オーディオビジュアライザー表示されるタイミングを制御します。",
"label": "オーディオビジュアライザーの表示"
"description": "オーディオビジュアライザー表示るタイミングを制御します。",
"label": "ビジュアライザーの表示設定"
}
},
"title": "デスクトップウィジェット",
"weather": {
"enabled": {
"description": "デスクトップに天気ウィジェットを表示す。",
"label": "天気ウィジェットを有効にする"
"description": "デスクトップに天気ウィジェットを表示します。",
"label": "天気ウィジェットを有効"
},
"show-background": {
"description": "天気ウィジェットの背景コンテナを表示す。",
"description": "天気ウィジェットの背景コンテナを表示します。",
"label": "背景を表示"
}
}
@@ -1517,7 +1516,7 @@
},
"temperature": {
"day": "日中",
"day-description": "昼間の色温度を調整します。",
"day-description": "日中の色温度を調整します。",
"description": "日中と夜間の色温度(暖かさ)を設定します。",
"label": "色温度",
"night": "夜間",
@@ -1541,8 +1540,8 @@
"label": "テーマカラーの適用"
},
"dead-opacity": {
"description": "実行されていないアプリアイコンの不透明度を調整します。",
"label": "死んだ不透明度"
"description": "起動していないアプリアイコンの不透明度を調整します。",
"label": "未実行時の不透明度"
},
"display": {
"always-visible": "常に表示",
@@ -1556,20 +1555,20 @@
"label": "画面端からの距離"
},
"hide-show-speed": {
"description": "ドックの表示/非表示アニメーションの速度を調整します。",
"label": "表示/非表示の速度"
"description": "ドックの表示非表示アニメーションの速度を調整します。",
"label": "表示非表示の速度"
},
"icon-size": {
"description": "ドック全体のサイズを調整します。",
"label": "ドックのサイズ"
},
"inactive-indicators": {
"description": "現在アクティブなアプリだけでなく、すべてのアプリインジケーターピルを表示す。",
"label": "実行指標"
"description": "現在アクティブなアプリだけでなく、すべてのアプリインジケーターを表示します。",
"label": "実行インジケーター"
},
"pinned-static": {
"description": "ピン留めされたアプリアイコンは、常に静的な順序で左に寄せてください。",
"label": "固定された静的アプリ"
"description": "ピン留めされたアプリアイコンを常に左側に寄せて固定します。",
"label": "ピン留めアプリの固定配置"
},
"section": {
"description": "ドックの動作と外観をカスタマイズします。",
@@ -1582,8 +1581,8 @@
},
"monitors": {
"only-same-monitor": {
"description": "ドックがあるモニターのアプリのみを表示す。",
"label": "同じモニターのアプリのみ"
"description": "ドックが配置されているディスプレイ上のアプリのみを表示します。",
"label": "同じディスプレイのアプリのみ"
},
"section": {
"description": "ドックを表示するディスプレイを選択します。未選択の場合は全てに表示されます。",
@@ -1677,12 +1676,12 @@
}
},
"performance-mode-disabled": {
"description": "Noctaliaパフォーマンスモードが無効になった際に実行されるコマンド。",
"description": "Noctalia パフォーマンスモードが無効になった際に実行されるコマンド。",
"label": "パフォーマンスモード無効時",
"placeholder": "例: notify-send \"Performance\" \"Mode Disabled\""
},
"performance-mode-enabled": {
"description": "Noctaliaパフォーマンスモードが有効になった際に実行されるコマンド。",
"description": "Noctalia パフォーマンスモードが有効になった際に実行されるコマンド。",
"label": "パフォーマンスモード有効時",
"placeholder": "例: notify-send \"Performance\" \"Mode Enabled\""
},
@@ -1692,7 +1691,7 @@
"placeholder": "例: notify-send \"Screen\" \"Locked\""
},
"screen-unlock": {
"description": "ロック画面が解除された際に実行されるコマンド。",
"description": "画面ロックが解除された際に実行されるコマンド。",
"label": "画面ロック解除時",
"placeholder": "例: notify-send \"Screen\" \"Unlocked\""
},
@@ -1718,6 +1717,10 @@
"placeholder": "例: notify-send \"Wallpaper\" \"Changed\""
}
},
"indicator": {
"default-value": "デフォルト: {value}",
"system-default": "システムのデフォルト"
},
"launcher": {
"settings": {
"clip-preview": {
@@ -1740,6 +1743,10 @@
"description": "リスト形式の代わりに、グリッド(格子状)レイアウトで項目を表示します。",
"label": "グリッド表示"
},
"icon-mode": {
"description": "Tablerアイコンではなく、ネイティブのシステムアイコンを使用します。",
"label": "ネイティブアイコンを使用"
},
"position": {
"description": "ランチャーパネルの表示位置を選択します。",
"label": "表示位置"
@@ -1749,7 +1756,7 @@
"label": "外観"
},
"show-categories": {
"description": "アプリケーションをフィルタリングするためのカテゴリタブを表示します。",
"description": "アプリケーションを絞り込むためのカテゴリタブを表示します。",
"label": "カテゴリを表示"
},
"sort-by-usage": {
@@ -1779,7 +1786,7 @@
"label": "カレンダーヘッダー"
},
"month": {
"label": "カレンダー"
"label": "カレンダー(月表示)"
}
},
"date-time": {
@@ -1858,13 +1865,14 @@
"label": "ハイバネートを表示"
},
"show-session-buttons": {
"description": "ロック画面から電源設定へのアクセスを許可します。",
"label": "電源制御"
"description": "ロック画面から電源操作を行えるようにします。",
"label": "電源操作"
},
"title": "ロック画面"
},
"network": {
"bluetooth": {
"description": "Bluetooth の管理を有効にします。",
"label": "Bluetooth を有効化"
},
"section": {
@@ -1872,6 +1880,7 @@
},
"title": "ネットワーク",
"wifi": {
"description": "ワイヤレスネットワークを管理します(NetworkManager が必要です)。",
"label": "Wi-Fi を有効化"
}
},
@@ -1937,57 +1946,57 @@
},
"sounds": {
"enabled": {
"description": "着信通知の音声効果を有効にします。",
"label": "通知音を有効にする"
"description": "通知受信時のの効果を有効にします。",
"label": "通知音を有効"
},
"excluded-apps": {
"description": "独自の通知音を持つ特定のアプリケーションについて、設定された通知音の再生をスキップします。",
"label": "除外アプリケーション",
"description": "独自の通知音を持つ特定のアプリケーションについて、設定た通知音の再生をスキップします。",
"label": "除外するアプリケーション",
"placeholder": "discord,firefox,chrome,chromium,edge"
},
"files": {
"critical": {
"description": "緊急度の高い通知で再生される音声ファイルのパス。",
"label": "緊急通知音",
"select-title": "緊急通知音ファイルを選択"
"description": "緊急度「重大」の通知で再生る音声ファイルのパス。",
"label": "緊急度「重大」の通知音",
"select-title": "緊急度「重大」の通知音ファイルを選択"
},
"low": {
"description": "緊急度の低い通知で再生される音声ファイルのパス。",
"label": "低優先度通知音",
"select-title": "低優先度通知音ファイルを選択"
"description": "緊急度「低」の通知で再生る音声ファイルのパス。",
"label": "緊急度「低」の通知音",
"select-title": "緊急度「低」の通知音ファイルを選択"
},
"normal": {
"description": "通常優先度の通知で再生される音声ファイルのパス。",
"label": "通常優先度通知音",
"select-title": "通常優先度通知音ファイルを選択"
"description": "緊急度「通常」の通知で再生る音声ファイルのパス。",
"label": "緊急度「通常」の通知音",
"select-title": "緊急度「通常」の通知音ファイルを選択"
},
"placeholder": "音声ファイルへのパスを入力",
"section": {
"description": "通知の緊急度レベルに応じてカスタム音声ファイルを設定します。",
"description": "通知の緊急度レベルごとにカスタム音声ファイルを設定します。",
"label": "音声ファイル"
},
"select-file": "音声ファイルを選択",
"unified": {
"description": "通知で再生される音声ファイルへのパス。",
"description": "通知で再生る音声ファイルへのパス。",
"label": "通知音",
"select-title": "通知音ファイルを選択"
}
},
"section": {
"description": "通知の音声効果と音量を設定します。",
"label": "サウンド設定"
"description": "通知の効果と音量を設定します。",
"label": "通知音設定"
},
"separate": {
"description": "低、通常、緊急優先度の通知に異なる音声ファイルを使用します。",
"label": "優先度ごとに異なる音を使用する"
"description": "緊急度(低、通常、重大)ごとに異なる音声ファイルを使用します。",
"label": "緊急度ごとに異なる通知音を使用"
},
"unavailable": {
"description": "通知音を有効にするにはQt6マルチメディアをインストールしてください。",
"label": "通知音利用できません"
"description": "通知音を有効にするにはQt6 Multimedia をインストールしてください。",
"label": "通知音利用できません"
},
"volume": {
"description": "通知音の音量レベルを調整します。",
"label": "サウンド音量"
"label": "通知音量"
}
},
"title": "通知",
@@ -2049,7 +2058,7 @@
"label": "画面の明るさ"
},
"custom-text": {
"description": "IPC からのカスタムテキストメッセージ OSD 表示します。",
"description": "IPC 経由のカスタムテキストメッセージ OSD 表示します。",
"label": "カスタムテキスト"
},
"input-volume": {
@@ -2057,7 +2066,7 @@
"label": "入力音量"
},
"lockkey": {
"description": "Caps Lock、Num Lock、Scroll Lock の切り替え時にOSDを表示します。",
"description": "Caps Lock、Num Lock、Scroll Lock の切り替え時に OSD を表示します。",
"label": "ロックキー"
},
"section": {
@@ -2072,32 +2081,32 @@
},
"plugins": {
"available": {
"description": "構成されたソースからプラグインをブラウズしてインストールします。",
"label": "利用できるプラグイン",
"description": "設定されたソースからプラグインを参照してインストールします。",
"label": "利用可能なプラグイン",
"no-plugins-description": "プラグインのソースを確認するか、リストを更新してください。",
"no-plugins-label": "利用できるプラグインはありません"
"no-plugins-label": "利用可能なプラグインはありません"
},
"filter": {
"all": "すべて",
"downloaded": "ダウンロード済み",
"not-downloaded": "ダウンロードされていません"
"not-downloaded": "ダウンロード"
},
"hot-reload": {
"description": "プラグインのファイル変更されたときに、自動的にプラグインを再読み込みします。プラグイン開発に便利です。",
"description": "ファイル変更時にプラグインを自動的にリロードします。プラグイン開発に便利です。",
"label": "ホットリロード(開発モード)"
},
"hot-reloaded": "プラグインを再読み込みしました: {name}",
"hot-reloaded": "プラグインをリロードしました: {name}",
"install": "インストール",
"install-error": "インストールに失敗しました: {error}",
"install-success": "{plugin} インストールに成功しました。",
"install-success": "{plugin} インストールしました。",
"installed": {
"description": "ローカルにインストールされたすべてのプラグインを管理および設定します。",
"description": "ローカルにインストールされたすべてのプラグインを管理設定します。",
"label": "インストール済みプラグイン",
"no-plugins-description": "下の利用可能なプラグインセクションからプラグインをインストールしてください。",
"no-plugins-label": "プラグインはインストールされていません"
"no-plugins-description": "下の利用可能なプラグインセクションからプラグインをインストールしてください。",
"no-plugins-label": "インストールされているプラグインはありません"
},
"installing": "{plugin} をインストール中...",
"plugin-settings-title": "{plugin} 設定",
"plugin-settings-title": "{plugin} 設定",
"refresh": {
"refreshing": "プラグインリストを更新中...",
"tooltip": "利用可能なプラグインを更新"
@@ -2105,22 +2114,22 @@
"settings": {
"tooltip": "プラグイン設定"
},
"settings-error-not-loaded": "プラグインはロードされませんでした。",
"settings-saved": "プラグイン設定を保存しました",
"settings-error-not-loaded": "プラグインは読み込まれていません",
"settings-saved": "プラグイン設定を保存しました",
"sources": {
"add-custom": "カスタムリポジトリを追加",
"add-dialog": {
"description": "GitHubリポジトリをプラグインソースとして追加する。",
"description": "プラグインソースとして GitHub リポジトリを追加します。",
"error": "プラグインソースの追加に失敗しました",
"name": "リポジトリ名",
"name-placeholder": "私のカスタムプラグイン",
"success": "プラグインソースが正常に追加されました",
"name-placeholder": "マイカスタムプラグイン",
"success": "プラグインソースを追加しました",
"title": "プラグインソースを追加",
"url": "リポジトリのURL"
"url": "リポジトリの URL"
},
"description": "プラグインリポジトリを管理す。",
"description": "プラグインリポジトリを管理します。",
"label": "ソース",
"placeholder": "私のクールなリポジトリ",
"placeholder": "マイリポジトリ",
"remove": {
"tooltip": "プラグインソースを削除"
}
@@ -2128,21 +2137,21 @@
"title": "プラグイン",
"uninstall": "アンインストール",
"uninstall-dialog": {
"description": "{plugin} をアンインストールしてもよろしいですか?プラグインデータはすべて削除されます。",
"title": "プラグインアンインストールする"
"description": "本当に {plugin} をアンインストールしますか?すべてのプラグインデータ削除されます。",
"title": "プラグインアンインストール"
},
"uninstall-error": "アンインストールに失敗しました: {error}",
"uninstall-success": "{plugin} アンインストールに成功しました。",
"uninstalling": "{plugin} をアンインストールしています...",
"update": "更新",
"update-all": "すべて更新 ({count})",
"update-all-success": "すべてのプラグインが正常に更新されました。",
"update-available": "{count}のプラグインアップデートがあります",
"update-error": "プラグインの更新に失敗しました: {plugin}: {error}",
"uninstall-success": "{plugin} アンインストールしました。",
"uninstalling": "{plugin} をアンインストール...",
"update": "アップデート",
"update-all": "すべてアップデート ({count})",
"update-all-success": "すべてのプラグインをアップデートしました。",
"update-available": "{count}のプラグインアップデートがあります",
"update-error": "プラグインのアップデートに失敗しました: {plugin}: {error}",
"update-incompatible": "Noctalia v{version} 以降が必要です。",
"update-success": "{plugin} を v{version} にアップデートしました。",
"update-version": "v{現在} → v{新規}",
"updating": "{plugin}を更新しています..."
"update-version": "v{current} → v{new}",
"updating": "{plugin} をアップデート中..."
},
"screen-recorder": {
"audio": {
@@ -2220,20 +2229,20 @@
},
"entry-settings": {
"command": {
"description": "このアクションのために実行するカスタムコマンド。空欄のままにするとデフォルトのシステムコマンド使用されます。",
"description": "このアクションのために実行するカスタムコマンド。空欄にするとデフォルトのシステムコマンド使用ます。",
"label": "コマンド",
"placeholder": "systemctl poweroff"
"placeholder": "例: systemctl poweroff"
},
"default-command": {
"lock": "内部ロック画面(コマンドなし)",
"logout": "内部ログアウト (コマンドなし)"
"logout": "内部ログアウトコマンドなし"
},
"default-info": {
"description": "カスタムコマンドが指定されていない場合、デフォルトのシステムコマンドが使用されます。",
"label": "デフォルトのコマンド"
},
"title": "{entry} を構成する",
"tooltip": "コマンドを構成する"
"title": "{entry}」の設定",
"tooltip": "コマンドを設定"
},
"general": {
"section": {
@@ -2242,7 +2251,7 @@
}
},
"large-buttons-style": {
"description": "セッションメニューを大きなボタンでグリッドレイアウトで表示する。",
"description": "セッションメニューを、グリッド配置の大きなボタンで表示します。",
"label": "大きなボタンのスタイル"
},
"position": {
@@ -2269,7 +2278,7 @@
"label": "ストレージ使用量"
},
"enable-nvidia-gpu": {
"description": "警告これにより NVIDIA GPU が起動、ハイブリッド グラフィックス搭載したラップトップのバッテリー寿命に大きな影響を与える可能性があります。",
"description": "警告: これにより NVIDIA GPU が起動状態になるため、ハイブリッドグラフィックス搭載のノート PC ではバッテリー駆動時間に大きな影響を与える可能性があります。",
"label": "NVIDIA GPU モニタリングを有効にする"
},
"general": {
@@ -2279,7 +2288,7 @@
}
},
"gpu-section": {
"label": "GPU温度 (GPU ondo)"
"label": "GPU 温度"
},
"highlight-colors-section": {
"label": "ハイライト色"
@@ -2297,8 +2306,8 @@
"label": "CPU 温度"
},
"threshold": {
"critical": "臨界閾値 (Rinkai kīchi)",
"warning": "警告しきい値"
"critical": "危険閾値",
"warning": "警告値"
},
"thresholds-section": {
"description": "各システムメトリックの警告/危険しきい値とポーリング間隔を調整します。",
@@ -2328,14 +2337,14 @@
"reset": "アニメーション速度をリセット"
},
"box-border-radius": {
"description": "サイドバー、カード、コンテンツパネルなど、主要なレイアウトセクションの角の丸みを調整します。",
"label": "コンテナ半径",
"reset": "コンテナ半径をリセット"
"description": "サイドバー、カード、コンテンツパネルなど、主要なレイアウト部分の角の丸みを調整します。",
"label": "コンテナの角の丸み",
"reset": "コンテナの角の丸みをリセット"
},
"control-border-radius": {
"description": "ボタン、トグル、テキストフィールドなどのインタラクティブな要素の曲率を制御します。",
"label": "入力半径",
"reset": "入力半径をリセット"
"description": "ボタン、トグル、テキストフィールドなど、操作可能な要素の角の丸みを制御します。",
"label": "入力要素の角の丸み",
"reset": "入力要素の角の丸みをリセット"
},
"dim-desktop": {
"description": "パネルやメニューが開いている間、デスクトップを暗くします。",
@@ -2368,8 +2377,8 @@
"label": "外観"
},
"settings-panel-mode": {
"description": "設定レイアウトを選択 (再起動が必要な場合があります)",
"label": "設定パネルモード"
"description": "設定画面の表示形式を選択します (再起動が必要な場合があります)",
"label": "設定パネルの表示モード"
},
"shadows": {
"description": "バーやパネルの下にドロップシャドウ(影)を表示します。",
@@ -2449,7 +2458,7 @@
"monitor-specific": {
"description": "ディスプレイごとに異なる壁紙フォルダを設定します。",
"label": "ディスプレイ別の壁紙フォルダ",
"tooltip": "モニター壁紙フォルダ"
"tooltip": "ディスプレイ別の壁紙フォルダ"
},
"recursive-search": {
"description": "壁紙フォルダのサブフォルダ内も検索対象にします。",
@@ -2580,7 +2589,7 @@
"noctalia-performance": {
"disabled": "パフォーマンスモード: 無効",
"enabled": "パフォーマンスモード: 有効",
"label": "Noctaliaパフォーマンス"
"label": "Noctalia パフォーマンス"
},
"power-profile": {
"changed": "電源プロファイルを変更しました",
@@ -2626,8 +2635,8 @@
"brightness-at": "画面の明るさ: {brightness}%",
"cancel-timer": "タイマー",
"clear-history": "履歴を消去",
"click-to-start-recording": "画面録画 (録画開始)",
"click-to-stop-recording": "画面録画 (録画停止)",
"click-to-start-recording": "画面録画録画開始",
"click-to-stop-recording": "画面録画録画停止",
"close": "閉じる",
"collapse": "サイドバーを折りたたむ",
"connect-disconnect-devices": "Bluetooth デバイス",
@@ -2642,7 +2651,7 @@
"hidden-files-hide": "隠しファイル",
"hidden-files-show": "隠しファイル",
"home": "ホームディレクトリ",
"input-muted": "入力ミュート切り替え",
"input-muted": "入力ミュート切り替え",
"keep-awake": "スリープ防止",
"keyboard-layout": "キーボードレイアウト: {layout}",
"list-view": "リスト表示",
@@ -2658,7 +2667,7 @@
"night-light-disabled": "夜間モード",
"night-light-enabled": "夜間モード",
"night-light-forced": "夜間モード",
"night-light-not-installed": "夜間モード (利用不可)",
"night-light-not-installed": "夜間モード利用不可",
"noctalia-performance-disabled": "Noctalia パフォーマンスモード",
"noctalia-performance-enabled": "Noctalia パフォーマンスモード",
"open-control-center": "コントロールセンター",
@@ -2667,7 +2676,7 @@
"open-settings": "設定",
"open-tray-dropdown": "システムトレイ",
"open-wallpaper-selector": "壁紙選択パネル",
"output-muted": "出力ミュート切り替え",
"output-muted": "出力ミュート切り替え",
"pause": "一時停止",
"play": "再生",
"power-profile": "電源プロファイル: {profile}",
@@ -2678,7 +2687,7 @@
"refresh-wallhaven": "Wallhaven の検索結果を更新",
"refresh-wallpaper-list": "壁紙リストを更新",
"remove-widget": "ウィジェットを削除",
"screen-recorder-not-installed": "スクリーンレコーダー (未インストール)",
"screen-recorder-not-installed": "スクリーンレコーダー未インストール",
"search": "検索",
"search-close": "検索を閉じる",
"session-menu": "セッションメニュー",
+19 -10
View File
@@ -527,6 +527,7 @@
"close-app": "Sluit {app}",
"connect-vpn": "Verbinding maken met {name}",
"cycle-visualizer": "Cyclusvisualisatie",
"delete": "Verwijderen",
"disable-bluetooth": "Bluetooth uitschakelen",
"disable-dnd": "Niet Storen uitschakelen",
"disable-wifi": "Wi-Fi uitschakelen",
@@ -735,10 +736,6 @@
"top_left": "Linksboven",
"top_right": "Rechtsboven"
},
"visualizer-quality": {
"high": "Hoog",
"low": "Laag"
},
"visualizer-types": {
"linear": "Lineair",
"mirrored": "Gespiegeld",
@@ -950,10 +947,6 @@
"description": "Stel je favoriete en genegeerde media-applicaties in.",
"label": "Mediaspelers"
},
"visualizer-quality": {
"description": "Een hogere kwaliteit gebruikt meer GPU-resources.",
"label": "Visualisatiekwaliteit"
},
"visualizer-type": {
"description": "Kies een visualisatietype voor mediaweergave.",
"label": "Type visualisatie"
@@ -1411,16 +1404,18 @@
}
},
"edit-mode": {
"add-widget": "Widget toevoegen",
"button": {
"label": "Ga naar de bewerkingsmodus"
},
"controls-explanation": "Linkermuisknop: Widget verplaatsen\nRechtermuisknop: Widget vergroten/verkleinen",
"controls-explanation": "Linksklikken en slepen: Widget verplaatsen of het formaat wijzigen.\nRechtermuisknop: Het contextmenu openen.",
"description": "Schakel de bewerkingsmodus in om desktopwidgets te verplaatsen en te herpositioneren. In de ingeschakelde modus tonen widgets een sleepomtrek en kunnen ze worden verplaatst.",
"exit-button": "Bewerkingsmodus verlaten",
"grid-snap": {
"label": "Raster uitlijnen"
},
"label": "Bewerkingsmodus"
"label": "Bewerkingsmodus",
"open-settings": "Instellingen openen"
},
"enabled": {
"description": "Desktopwidgets volledig in- of uitschakelen.",
@@ -1445,6 +1440,10 @@
"description": "Toon de achtergrondcontainer voor de mediaspeler-widget.",
"label": "Toon achtergrond"
},
"show-buttons": {
"description": "Toon mediabesturingsknoppen (afspelen/pauzeren, vorige, volgende) op de mediaspeler-widget.",
"label": "Knoppen weergeven"
},
"visualizer-type": {
"description": "Kies een visualisatietype voor de achtergrond van de desktop mediaspeler.",
"label": "Visualisatietype"
@@ -1718,6 +1717,10 @@
"placeholder": "bijv. notify-send \"Achtergrond\" \"Gewijzigd\""
}
},
"indicator": {
"default-value": "Standaard: {value}",
"system-default": "Systeemstandaard"
},
"launcher": {
"settings": {
"clip-preview": {
@@ -1740,6 +1743,10 @@
"description": "Items in een raster weergeven in plaats van een lijst.",
"label": "Rasterweergave"
},
"icon-mode": {
"description": "Gebruik native systeemiconen in plaats van Tabler-iconen.",
"label": "Gebruik native iconen"
},
"position": {
"description": "Kies waar het launcher-paneel verschijnt.",
"label": "Positie"
@@ -1865,6 +1872,7 @@
},
"network": {
"bluetooth": {
"description": "Bluetooth-beheer activeren.",
"label": "Bluetooth inschakelen"
},
"section": {
@@ -1872,6 +1880,7 @@
},
"title": "Netwerk",
"wifi": {
"description": "Draadloze netwerken beheren (vereist NetworkManager).",
"label": "Wi-Fi inschakelen"
}
},
+19 -10
View File
@@ -527,6 +527,7 @@
"close-app": "Fechar {app}",
"connect-vpn": "Conectar-se a {name}",
"cycle-visualizer": "Visualizador de ciclo",
"delete": "Apagar",
"disable-bluetooth": "Desativar Bluetooth",
"disable-dnd": "Desativar o Não Perturbe",
"disable-wifi": "Desativar Wi-Fi",
@@ -735,10 +736,6 @@
"top_left": "Superior esquerda",
"top_right": "Superior direita"
},
"visualizer-quality": {
"high": "Alto(a)",
"low": "Baixo(a)"
},
"visualizer-types": {
"linear": "Linear",
"mirrored": "Espelhado",
@@ -950,10 +947,6 @@
"description": "Defina seus aplicativos de mídia preferidos e ignorados.",
"label": "Reprodutores de mídia"
},
"visualizer-quality": {
"description": "Alta qualidade usa mais recursos da GPU.",
"label": "Qualidade da visualização"
},
"visualizer-type": {
"description": "Escolha um tipo de visualização para a reprodução de mídia",
"label": "Tipo de visualização"
@@ -1411,16 +1404,18 @@
}
},
"edit-mode": {
"add-widget": "Adicionar widget",
"button": {
"label": "Entrar no modo de edição"
},
"controls-explanation": "Botão esquerdo do mouse: Mover widget\nBotão direito do mouse: Redimensionar widget",
"controls-explanation": "Clique esquerdo e arraste: Mover ou redimensionar o widget.\nClique direito: Abrir as opções do menu de contexto.",
"description": "Ative o modo de edição para mover e reposicionar widgets da área de trabalho. Quando ativado, os widgets exibem um contorno de arrastar e podem ser reposicionados.",
"exit-button": "Sair do modo de edição",
"grid-snap": {
"label": "Alinhar à grade"
},
"label": "Modo de edição"
"label": "Modo de edição",
"open-settings": "Abrir configurações"
},
"enabled": {
"description": "Ativar ou desativar widgets da área de trabalho completamente.",
@@ -1445,6 +1440,10 @@
"description": "Mostrar o contêiner de fundo para o widget do reprodutor de mídia.",
"label": "Mostrar plano de fundo"
},
"show-buttons": {
"description": "Mostrar botões de controle de mídia (reproduzir/pausar, anterior, próximo) no widget do reprodutor de mídia.",
"label": "Mostrar botões"
},
"visualizer-type": {
"description": "Escolha um tipo de visualização para o fundo do reprodutor de mídia na área de trabalho.",
"label": "Tipo de visualização"
@@ -1718,6 +1717,10 @@
"placeholder": "ex., notify-send \"Papel de parede\" \"Alterado\""
}
},
"indicator": {
"default-value": "Padrão: {value}",
"system-default": "Padrão do sistema"
},
"launcher": {
"settings": {
"clip-preview": {
@@ -1740,6 +1743,10 @@
"description": "Exibir itens em uma grade em vez de uma lista.",
"label": "Visualização em grade"
},
"icon-mode": {
"description": "Use ícones nativos do sistema em vez de ícones Tabler.",
"label": "Usar ícones nativos"
},
"position": {
"description": "Escolha onde o painel do lançador aparece.",
"label": "Posição"
@@ -1865,6 +1872,7 @@
},
"network": {
"bluetooth": {
"description": "Ativar gerenciamento de Bluetooth.",
"label": "Ativar Bluetooth"
},
"section": {
@@ -1872,6 +1880,7 @@
},
"title": "Rede",
"wifi": {
"description": "Gerenciar redes sem fio (requer NetworkManager).",
"label": "Ativar Wi-Fi"
}
},
+19 -10
View File
@@ -527,6 +527,7 @@
"close-app": "Закрыть {app}",
"connect-vpn": "Подключиться к {name}",
"cycle-visualizer": "Визуализатор циклов",
"delete": "Удалить",
"disable-bluetooth": "Отключить Bluetooth",
"disable-dnd": "Отключить режим \"Не беспокоить\"",
"disable-wifi": "Отключить Wi-Fi",
@@ -735,10 +736,6 @@
"top_left": "Вверху слева",
"top_right": "Вверху справа"
},
"visualizer-quality": {
"high": "Высокое",
"low": "Низкое"
},
"visualizer-types": {
"linear": "Линейный",
"mirrored": "Зеркальный",
@@ -950,10 +947,6 @@
"description": "Установите предпочитаемые и игнорируемые медиа-приложения.",
"label": "Медиаплееры"
},
"visualizer-quality": {
"description": "Высокое качество использует больше ресурсов GPU.",
"label": "Качество визуализации"
},
"visualizer-type": {
"description": "Выберите тип визуализации для воспроизведения медиа.",
"label": "Тип визуализации"
@@ -1411,16 +1404,18 @@
}
},
"edit-mode": {
"add-widget": "Добавить виджет",
"button": {
"label": "Войти в режим редактирования"
},
"controls-explanation": "Левая кнопка мыши: Перемещать виджет\nПравая кнопка мыши: Изменять размер виджета",
"controls-explanation": "Левый клик и перетаскивание: Переместить или изменить размер виджета.\nПравый клик: Открыть контекстное меню.",
"description": "Включите режим редактирования, чтобы перемещать и изменять положение виджетов рабочего стола. В этом режиме виджеты отображаются с контуром перетаскивания и могут быть перемещены.",
"exit-button": "Выйти из режима редактирования",
"grid-snap": {
"label": "Привязка к сетке"
},
"label": "Режим редактирования"
"label": "Режим редактирования",
"open-settings": "Открыть настройки"
},
"enabled": {
"description": "Включить или отключить виджеты рабочего стола полностью.",
@@ -1445,6 +1440,10 @@
"description": "Показать фоновый контейнер для виджета медиаплеера.",
"label": "Показать фон"
},
"show-buttons": {
"description": "Показать кнопки управления медиа (воспроизведение/пауза, предыдущий, следующий) на виджете медиаплеера.",
"label": "Показать кнопки"
},
"visualizer-type": {
"description": "Выберите тип визуализации для фона медиаплеера.",
"label": "Тип визуализации"
@@ -1718,6 +1717,10 @@
"placeholder": "например, notify-send \"Wallpaper\" \"Changed\""
}
},
"indicator": {
"default-value": "По умолчанию: {value}",
"system-default": "По умолчанию"
},
"launcher": {
"settings": {
"clip-preview": {
@@ -1740,6 +1743,10 @@
"description": "Показывать элементы в виде сетки вместо списка.",
"label": "Вид сетки"
},
"icon-mode": {
"description": "Использовать нативные системные иконки вместо иконок Tabler.",
"label": "Использовать нативные иконки"
},
"position": {
"description": "Выберите, где появляется панель запуска.",
"label": "Положение"
@@ -1865,6 +1872,7 @@
},
"network": {
"bluetooth": {
"description": "Включить управление Bluetooth.",
"label": "Включить Bluetooth"
},
"section": {
@@ -1872,6 +1880,7 @@
},
"title": "Сеть",
"wifi": {
"description": "Управление беспроводными сетями (требуется NetworkManager).",
"label": "Включить Wi-Fi"
}
},
+19 -10
View File
@@ -527,6 +527,7 @@
"close-app": "{app}'i kapat",
"connect-vpn": "{name} bağlantısına bağlan",
"cycle-visualizer": "Döngü görselleştirici",
"delete": "Sil",
"disable-bluetooth": "Bluetooth'u kapat",
"disable-dnd": "Rahatsız Etmeyin'i Kapat",
"disable-wifi": "Kablosuz Bağlantıyı kapat",
@@ -735,10 +736,6 @@
"top_left": "Sol üst",
"top_right": "Sağ üst"
},
"visualizer-quality": {
"high": "Yüksek",
"low": "Düşük"
},
"visualizer-types": {
"linear": "Doğrusal",
"mirrored": "Yansıtılmış",
@@ -950,10 +947,6 @@
"description": "Tercih ettiğiniz ve yoksayacağınız ortam uygulamalarını ayarlayın.",
"label": "Ortam oynatıcıları"
},
"visualizer-quality": {
"description": "Yüksek kalite ekran kartını daha fazla kullanır.",
"label": "Görselleştirme kalitesi"
},
"visualizer-type": {
"description": "Ortam oynatımı için bir görselleştirme türü seçin",
"label": "Görselleştirme türü"
@@ -1411,16 +1404,18 @@
}
},
"edit-mode": {
"add-widget": "Araç ekle",
"button": {
"label": "Düzenleme moduna gir"
},
"controls-explanation": "Sol fare tuşu: Araç taşı\nSağ fare tuşu: Araç yeniden boyutlandır",
"controls-explanation": "Sol tıklayıp sürükleyin: Bileşeni taşıyın veya yeniden boyutlandırın.\nSağ tıklayın: Bağlam menüsü seçeneklerini açın.",
"description": "Masaüstü araçlarını taşımak ve yeniden konumlandırmak için düzenleme modunu etkinleştirin. Etkinleştirildiğinde, araçlar bir sürükleme ana hattı gösterir ve yeniden konumlandırılabilir.",
"exit-button": "Düzenleme modundan çık",
"grid-snap": {
"label": "Izgaraya hizala"
},
"label": "Düzenleme modu"
"label": "Düzenleme modu",
"open-settings": "Ayarları aç"
},
"enabled": {
"description": "Masaüstü araçlarını tamamen etkinleştir veya devre dışı bırak.",
@@ -1445,6 +1440,10 @@
"description": "Medya oynatıcı widget'ı için arka plan konteynerini göster.",
"label": "Arka planı göster"
},
"show-buttons": {
"description": "Medya oynatıcı widget'ında medya kontrol düğmelerini (oynat/durdur, önceki, sonraki) göster.",
"label": "Düğmeleri göster"
},
"visualizer-type": {
"description": "Masaüstü medya oynatıcı arka planı için bir görselleştirme türü seçin.",
"label": "Görselleştirme türü"
@@ -1718,6 +1717,10 @@
"placeholder": "örn., notify-send \"Duvar Kâğıdı\" \"Değişti\""
}
},
"indicator": {
"default-value": "Varsayılan: {value}",
"system-default": "Sistem Varsayılanı"
},
"launcher": {
"settings": {
"clip-preview": {
@@ -1740,6 +1743,10 @@
"description": "Öğeleri liste yerine ızgara düzeninde görüntüle.",
"label": "Izgara görünümü"
},
"icon-mode": {
"description": "Tabler simgeleri yerine yerel sistem simgelerini kullanın.",
"label": "Yerel simgeleri kullan"
},
"position": {
"description": "Başlatıcı panelinin nerede görüneceğini seçin.",
"label": "Konum"
@@ -1865,6 +1872,7 @@
},
"network": {
"bluetooth": {
"description": "Bluetooth yönetimini etkinleştir.",
"label": "Bluetooth'u Etkinleştir"
},
"section": {
@@ -1872,6 +1880,7 @@
},
"title": "Ağ",
"wifi": {
"description": "Kablosuz ağları yönet (NetworkManager gerektirir).",
"label": "Kablosuz Bağlantıyı Etkinleştir"
}
},
+19 -10
View File
@@ -527,6 +527,7 @@
"close-app": "Закрити {app}",
"connect-vpn": "Підключитися до {name}",
"cycle-visualizer": "Візуалізатор циклів",
"delete": "Видалити",
"disable-bluetooth": "Вимкнути Bluetooth",
"disable-dnd": "Вимкнути режим \"Не турбувати\"",
"disable-wifi": "Вимкнути Wi-Fi",
@@ -735,10 +736,6 @@
"top_left": "Згори ліворуч",
"top_right": "Згори праворуч"
},
"visualizer-quality": {
"high": "Високий",
"low": "Низький"
},
"visualizer-types": {
"linear": "Лінійний",
"mirrored": "Дзеркальний",
@@ -950,10 +947,6 @@
"description": "Встановіть бажані та ігноровані медіазастосунки.",
"label": "Медіаплеєри"
},
"visualizer-quality": {
"description": "Висока якість використовує більше ресурсів GPU.",
"label": "Якість візуалізації"
},
"visualizer-type": {
"description": "Виберіть тип візуалізації для відтворення медіа",
"label": "Тип візуалізації"
@@ -1411,16 +1404,18 @@
}
},
"edit-mode": {
"add-widget": "Додати віджет",
"button": {
"label": "Увійти в режим редагування"
},
"controls-explanation": "Ліва кнопка миші: Переміщувати віджет\nПрава кнопка миші: Змінювати розмір віджета",
"controls-explanation": "Клацання лівою кнопкою миші та перетягування: Перемістити або змінити розмір віджета.\nКлацання правою кнопкою миші: Відкрити контекстне меню з опціями.",
"description": "Увімкніть режим редагування, щоб переміщувати та змінювати розташування віджетів робочого столу. У ввімкненому стані віджети відображають контур перетягування, і їх можна переміщувати.",
"exit-button": "Вийти з режиму редагування",
"grid-snap": {
"label": "Прив'язка до сітки"
},
"label": "Режим редагування"
"label": "Режим редагування",
"open-settings": "Відкрити налаштування"
},
"enabled": {
"description": "Увімкнути або вимкнути віджети робочого столу повністю.",
@@ -1445,6 +1440,10 @@
"description": "Показати фоновий контейнер для віджета медіаплеєра.",
"label": "Показати фон"
},
"show-buttons": {
"description": "Показати кнопки керування медіа (відтворення/пауза, попередній, наступний) на віджеті медіаплеєра.",
"label": "Показати кнопки"
},
"visualizer-type": {
"description": "Оберіть тип візуалізації для фону медіаплеєра на робочому столі.",
"label": "Тип візуалізації"
@@ -1718,6 +1717,10 @@
"placeholder": "напр., notify-send \"Шпалери\" \"Змінено\""
}
},
"indicator": {
"default-value": "За замовчуванням: {value}",
"system-default": "Системні налаштування за замовчуванням"
},
"launcher": {
"settings": {
"clip-preview": {
@@ -1740,6 +1743,10 @@
"description": "Показувати елементи у вигляді сітки замість списку.",
"label": "Режим сітки"
},
"icon-mode": {
"description": "Використовувати нативні системні іконки замість іконок Tabler.",
"label": "Використовувати нативні іконки"
},
"position": {
"description": "Виберіть, де з'являється панель лаунчера.",
"label": "Положення"
@@ -1865,6 +1872,7 @@
},
"network": {
"bluetooth": {
"description": "Активувати керування Bluetooth.",
"label": "Увімкнути Bluetooth"
},
"section": {
@@ -1872,6 +1880,7 @@
},
"title": "Мережа",
"wifi": {
"description": "Керування бездротовими мережами (потребує NetworkManager).",
"label": "Увімкнути Wi-Fi"
}
},
+19 -10
View File
@@ -527,6 +527,7 @@
"close-app": "关闭 {app}",
"connect-vpn": "连接 {name}",
"cycle-visualizer": "切换可视化器样式",
"delete": "删除",
"disable-bluetooth": "禁用蓝牙",
"disable-dnd": "关闭勿扰模式",
"disable-wifi": "禁用Wi-Fi",
@@ -735,10 +736,6 @@
"top_left": "左上",
"top_right": "右上"
},
"visualizer-quality": {
"high": "高",
"low": "低"
},
"visualizer-types": {
"linear": "线性",
"mirrored": "镜像",
@@ -950,10 +947,6 @@
"description": "设置您首选和要忽略的媒体应用程序。",
"label": "媒体播放器"
},
"visualizer-quality": {
"description": "高质量使用更多 GPU 资源。",
"label": "可视化质量"
},
"visualizer-type": {
"description": "选择媒体播放的可视化类型",
"label": "可视化类型"
@@ -1411,16 +1404,18 @@
}
},
"edit-mode": {
"add-widget": "添加小部件",
"button": {
"label": "进入编辑模式"
},
"controls-explanation": "鼠标左键:移动小组件\n鼠标右键:调整小组件大小",
"controls-explanation": "左键点击并拖动:移动或调整小部件大小。\n右键点击:打开上下文菜单选项。",
"description": "启用编辑模式以移动和重新定位桌面小组件。启用后,小组件会显示拖动轮廓,并且可以重新定位。",
"exit-button": "退出编辑模式",
"grid-snap": {
"label": "网格对齐"
},
"label": "编辑模式"
"label": "编辑模式",
"open-settings": "打开设置"
},
"enabled": {
"description": "完全启用或禁用桌面小部件。",
@@ -1445,6 +1440,10 @@
"description": "显示媒体播放器小部件的背景容器。",
"label": "显示背景"
},
"show-buttons": {
"description": "在媒体播放器小部件上显示媒体控制按钮(播放/暂停、上一个、下一个)。",
"label": "显示按钮"
},
"visualizer-type": {
"description": "选择桌面媒体播放器的背景可视化类型。",
"label": "可视化类型"
@@ -1718,6 +1717,10 @@
"placeholder": "例如:notify-send \"壁纸\" \"已更改\""
}
},
"indicator": {
"default-value": "默认值:{value}",
"system-default": "系统默认"
},
"launcher": {
"settings": {
"clip-preview": {
@@ -1740,6 +1743,10 @@
"description": "以网格布局而非列表显示项目。",
"label": "网格视图"
},
"icon-mode": {
"description": "使用本地系统图标而不是 Tabler 图标。",
"label": "使用本地图标"
},
"position": {
"description": "选择启动器面板出现的位置。",
"label": "位置"
@@ -1865,6 +1872,7 @@
},
"network": {
"bluetooth": {
"description": "启用蓝牙管理。",
"label": "启用蓝牙"
},
"section": {
@@ -1872,6 +1880,7 @@
},
"title": "网络",
"wifi": {
"description": "管理无线网络(需要 NetworkManager)。",
"label": "启用 Wi-Fi"
}
},
+2 -3
View File
@@ -179,7 +179,8 @@
"customLaunchPrefixEnabled": false,
"customLaunchPrefix": "",
"viewMode": "list",
"showCategories": true
"showCategories": true,
"iconMode": "tabler"
},
"controlCenter": {
"position": "close_to_bar_button",
@@ -353,7 +354,6 @@
"volumeOverdrive": false,
"cavaFrameRate": 30,
"visualizerType": "linear",
"visualizerQuality": "high",
"mprisBlacklist": [],
"preferredPlayer": "",
"externalMixer": "pwvucontrol || pavucontrol"
@@ -418,7 +418,6 @@
},
"desktopWidgets": {
"enabled": false,
"editMode": false,
"gridSnap": false,
"monitorWidgets": []
}
+2 -2
View File
@@ -387,6 +387,8 @@ Singleton {
// View mode: "list" or "grid"
property string viewMode: "list"
property bool showCategories: true
// Icon mode: "tabler" or "native"
property string iconMode: "tabler"
}
// control center
@@ -575,7 +577,6 @@ Singleton {
property bool volumeOverdrive: false
property int cavaFrameRate: 30
property string visualizerType: "linear"
property string visualizerQuality: "high"
property list<string> mprisBlacklist: []
property string preferredPlayer: ""
property string externalMixer: "pwvucontrol || pavucontrol"
@@ -651,7 +652,6 @@ Singleton {
// desktop widgets
property JsonObject desktopWidgets: JsonObject {
property bool enabled: false
property bool editMode: false
property bool gridSnap: false
property list<var> monitorWidgets: []
// Format: [{ "name": "DP-1", "widgets": [...] }, { "name": "HDMI-1", "widgets": [...] }]
+1
View File
@@ -212,6 +212,7 @@ Singleton {
barVisible: BarService.isVisible,
lockScreenActive: PanelService.lockScreen?.active || false,
wallpapers: WallpaperService.currentWallpapers || {},
desktopWidgetsEditMode: DesktopWidgetRegistry.editMode || false,
// -------------
display: shellStateData.display || {},
notificationsState: shellStateData.notificationsState || {},
+1 -25
View File
@@ -29,23 +29,6 @@ Item {
return (item && item.visible) ? Math.round(item[prop]) : 0;
}
// Create a dummy pluginApi that returns empty strings to avoid undefined warnings
property var _dummyApi: QtObject {
property var pluginSettings: ({})
property var manifest: ({
metadata: {
defaultSettings: {}
}
})
function tr(key) {
return "";
}
function trp(key, count) {
return "";
}
}
// Only load if widget exists in registry
function checkWidgetExists(): bool {
return root.widgetId !== "" && BarWidgetRegistry.hasWidget(root.widgetId);
@@ -84,13 +67,6 @@ Item {
if (!item)
return;
// Inject dummy API immediately for plugin widgets before any other code runs
if (BarWidgetRegistry.isPluginWidget(widgetId) && item.hasOwnProperty("pluginApi")) {
if (!item.pluginApi) {
item.pluginApi = root._dummyApi;
}
}
Logger.d("BarWidgetLoader", "Loading widget", widgetId, "on screen:", widgetScreen.name);
// Apply properties to loaded widget
@@ -113,11 +89,11 @@ Item {
}
// Inject plugin API for plugin widgets
// The API is fully populated (settings/translations already loaded) by PluginService
if (BarWidgetRegistry.isPluginWidget(widgetId)) {
var pluginId = widgetId.replace("plugin:", "");
var api = PluginService.getPluginAPI(pluginId);
if (api && item.hasOwnProperty("pluginApi")) {
// Inject API into widget
item.pluginApi = api;
Logger.d("BarWidgetLoader", "Injected plugin API for", widgetId);
}
+17
View File
@@ -55,6 +55,23 @@ Item {
readonly property bool shouldShow: (currentVisualizerType !== "" && currentVisualizerType !== "none") && (!hideWhenIdle || MediaService.isPlaying)
// Register/unregister with CavaService based on visibility
readonly property string cavaComponentId: "bar:audiovisualizer:" + root.screen.name + ":" + root.section + ":" + root.sectionWidgetIndex
onShouldShowChanged: {
if (root.shouldShow) {
CavaService.registerComponent(root.cavaComponentId);
} else {
CavaService.unregisterComponent(root.cavaComponentId);
}
}
Component.onDestruction: {
if (root.shouldShow) {
CavaService.unregisterComponent(root.cavaComponentId);
}
}
implicitWidth: !shouldShow ? 0 : isVerticalBar ? Style.capsuleHeight : visualizerWidth
implicitHeight: !shouldShow ? 0 : isVerticalBar ? visualizerWidth : Style.capsuleHeight
visible: shouldShow
+18
View File
@@ -66,6 +66,24 @@ Item {
return showArtistFirst ? (artist ? `${artist} - ${track}` : track) : (artist ? `${track} - ${artist}` : track);
}
// CavaService registration for visualizer
readonly property string cavaComponentId: "bar:mediamini:" + root.screen.name + ":" + root.section + ":" + root.sectionWidgetIndex
readonly property bool needsCava: root.showVisualizer && root.visualizerType !== "" && root.visualizerType !== "none"
onNeedsCavaChanged: {
if (root.needsCava) {
CavaService.registerComponent(root.cavaComponentId);
} else {
CavaService.unregisterComponent(root.cavaComponentId);
}
}
Component.onDestruction: {
if (root.needsCava) {
CavaService.unregisterComponent(root.cavaComponentId);
}
}
readonly property string tooltipText: {
var text = title;
var controls = [];
+3 -3
View File
@@ -632,7 +632,7 @@ Item {
anchors.fill: parent
Loader {
active: (labelMode !== "none") && (!root.showLabelsOnlyWhenOccupied || model.isOccupied)
active: (labelMode !== "none") && (!root.showLabelsOnlyWhenOccupied || model.isOccupied || model.isFocused)
sourceComponent: Component {
NText {
x: (pillVertical.width - width) / 2
@@ -662,7 +662,7 @@ Item {
if (model.isOccupied)
return Color.mOnSecondary;
return Color.mOnSurface;
return Color.mOnSecondary;
}
}
}
@@ -677,7 +677,7 @@ Item {
if (model.isOccupied)
return Color.mSecondary;
return Color.mOutline;
return Qt.alpha(Color.mSecondary, 0.3);
}
z: 0
+21
View File
@@ -15,6 +15,27 @@ NBox {
// Track whether we have an active media player
readonly property bool hasActivePlayer: MediaService.currentPlayer && MediaService.canPlay
// CavaService registration for visualizer
readonly property bool needsCava: Settings.data.audio.visualizerType !== "" && Settings.data.audio.visualizerType !== "none"
onNeedsCavaChanged: {
if (root.needsCava) {
CavaService.registerComponent("mediacard");
} else {
CavaService.unregisterComponent("mediacard");
}
}
Component.onCompleted: {
if (root.needsCava) {
CavaService.registerComponent("mediacard");
}
}
Component.onDestruction: {
CavaService.unregisterComponent("mediacard");
}
property string wallpaper: WallpaperService.getWallpaper(screen.name)
// External state management
+208 -104
View File
@@ -4,6 +4,7 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
import qs.Commons
import qs.Modules.Panels.Settings
import qs.Services.Compositor
import qs.Services.Noctalia
import qs.Services.Power
@@ -70,6 +71,57 @@ Variants {
Logger.d("DesktopWidgets", "Created panel window for", screen?.name);
}
// Add a new widget to the current screen
function addWidgetToCurrentScreen(widgetId) {
var monitorName = window.screen.name;
var newWidget = {
"id": widgetId
};
// Load default metadata if available
var metadata = DesktopWidgetRegistry.widgetMetadata[widgetId];
if (metadata) {
Object.keys(metadata).forEach(function (key) {
if (key !== "allowUserSettings") {
newWidget[key] = metadata[key];
}
});
}
// Place at screen center
newWidget.x = (window.screen.width / 2) - 100;
newWidget.y = (window.screen.height / 2) - 100;
newWidget.scale = 1.0;
// Get current widgets and add new one
var monitorWidgets = Settings.data.desktopWidgets.monitorWidgets || [];
var newMonitorWidgets = monitorWidgets.slice();
var found = false;
for (var i = 0; i < newMonitorWidgets.length; i++) {
if (newMonitorWidgets[i].name === monitorName) {
var widgets = (newMonitorWidgets[i].widgets || []).slice();
widgets.push(newWidget);
newMonitorWidgets[i] = {
"name": monitorName,
"widgets": widgets
};
found = true;
break;
}
}
if (!found) {
newMonitorWidgets.push({
"name": monitorName,
"widgets": [newWidget]
});
}
Settings.data.desktopWidgets.monitorWidgets = newMonitorWidgets;
Logger.i("DesktopWidgets", "Added widget", widgetId, "to", monitorName);
}
Item {
id: widgetsContainer
anchors.fill: parent
@@ -78,7 +130,7 @@ Variants {
// Using Loader to properly unload Canvas when not needed
Loader {
id: gridOverlayLoader
active: Settings.data.desktopWidgets.editMode && Settings.data.desktopWidgets.enabled && Settings.data.desktopWidgets.gridSnap
active: DesktopWidgetRegistry.editMode && Settings.data.desktopWidgets.enabled && Settings.data.desktopWidgets.gridSnap
anchors.fill: parent
z: -1 // Behind widgets but above background
asynchronous: false
@@ -197,6 +249,10 @@ Variants {
gridOverlay.requestPaint();
}
}
}
Connections {
target: DesktopWidgetRegistry
function onEditModeChanged() {
if (gridOverlayLoader.active) {
gridOverlay.requestPaint();
@@ -245,140 +301,188 @@ Variants {
}
}
// Background for edit mode controls
// Edit mode controls panel
Rectangle {
id: editModeControlsBackground
visible: Settings.data.desktopWidgets.editMode && Settings.data.desktopWidgets.enabled
id: editModeControlsPanel
visible: DesktopWidgetRegistry.editMode && Settings.data.desktopWidgets.enabled
readonly property string barPos: Settings.data.bar.position || "top"
readonly property bool barFloating: Settings.data.bar.floating || false
// Calculate offset from bar based on position and floating state
readonly property int barOffsetTop: {
if (barPos !== "top")
return Style.marginXL * Style.uiScaleRatio;
return Style.marginM;
const floatMarginV = barFloating ? Math.ceil(Settings.data.bar.marginVertical * Style.marginXL) : 0;
return Style.barHeight + floatMarginV + Style.marginM + (Style.marginXL * Style.uiScaleRatio);
return Style.barHeight + floatMarginV + Style.marginM;
}
readonly property int barOffsetRight: {
if (barPos !== "right")
return Style.marginXL * Style.uiScaleRatio;
return Style.marginM;
const floatMarginH = barFloating ? Math.ceil(Settings.data.bar.marginHorizontal * Style.marginXL) : 0;
return Style.barHeight + floatMarginH + Style.marginM + (Style.marginXL * Style.uiScaleRatio);
return Style.barHeight + floatMarginH + Style.marginM;
}
anchors {
top: parent.top
right: parent.right
topMargin: barOffsetTop
rightMargin: barOffsetRight
// Internal state for drag tracking (session-only, resets on restart)
QtObject {
id: panelInternal
property bool isDragging: false
property real dragOffsetX: 0
property real dragOffsetY: 0
// Default position: top-right corner accounting for bar
property real baseX: widgetsContainer.width - editModeControlsPanel.width - editModeControlsPanel.barOffsetRight
property real baseY: editModeControlsPanel.barOffsetTop
}
// Calculate width to accommodate all controls
width: {
var buttonWidth = editModeButton.visible ? editModeButton.implicitWidth : 0;
var explanationWidth = controlsExplanation.visible ? controlsExplanation.width : 0;
var checkboxWidth = gridSnapCheckbox.visible ? gridSnapCheckbox.implicitWidth : 0;
return Math.max(buttonWidth, explanationWidth, checkboxWidth, 200) + (Style.marginXL * 2);
// Reset position when bar position changes
Connections {
target: Settings.data.bar
function onPositionChanged() {
panelInternal.baseX = widgetsContainer.width - editModeControlsPanel.width - editModeControlsPanel.barOffsetRight;
panelInternal.baseY = editModeControlsPanel.barOffsetTop;
}
}
// Calculate height to cover all controls with spacing
height: {
var buttonHeight = editModeButton.visible ? editModeButton.height : 0;
var explanationHeight = controlsExplanation.visible ? controlsExplanation.height : 0;
var checkboxHeight = gridSnapCheckbox.visible ? gridSnapCheckbox.height : 0;
return buttonHeight + Style.marginXL + explanationHeight + Style.marginXL + checkboxHeight + (Style.marginXL * 2);
}
x: panelInternal.isDragging ? panelInternal.dragOffsetX : panelInternal.baseX
y: panelInternal.isDragging ? panelInternal.dragOffsetY : panelInternal.baseY
width: controlsLayout.implicitWidth + (Style.marginXL * 2)
height: controlsLayout.implicitHeight + (Style.marginXL * 2)
color: Qt.rgba(Color.mSurface.r, Color.mSurface.g, Color.mSurface.b, 0.85)
radius: Style.radiusL
border {
width: 1
color: Qt.alpha(Color.mOutline, 0.2)
width: Style.borderS
color: Color.mOutline
}
z: 9999
}
// Exit edit mode button
NButton {
id: editModeButton
visible: Settings.data.desktopWidgets.editMode && Settings.data.desktopWidgets.enabled
// Drag area for relocating the panel
MouseArea {
id: dragArea
anchors.fill: parent
cursorShape: panelInternal.isDragging ? Qt.ClosedHandCursor : Qt.OpenHandCursor
readonly property string barPos: Settings.data.bar.position || "top"
readonly property bool barFloating: Settings.data.bar.floating || false
// Calculate offset from bar based on position and floating state
readonly property int barOffsetTop: {
if (barPos !== "top")
return Style.marginXL * Style.uiScaleRatio;
const floatMarginV = barFloating ? Math.ceil(Settings.data.bar.marginVertical * Style.marginXL) : 0;
return Style.barHeight + floatMarginV + Style.marginM + (Style.marginXL * Style.uiScaleRatio);
}
readonly property int barOffsetRight: {
if (barPos !== "right")
return Style.marginXL * Style.uiScaleRatio;
const floatMarginH = barFloating ? Math.ceil(Settings.data.bar.marginHorizontal * Style.marginXL) : 0;
return Style.barHeight + floatMarginH + Style.marginM + (Style.marginXL * Style.uiScaleRatio);
property point pressPos: Qt.point(0, 0)
onPressed: mouse => {
pressPos = mapToItem(widgetsContainer, mouse.x, mouse.y);
panelInternal.dragOffsetX = editModeControlsPanel.x;
panelInternal.dragOffsetY = editModeControlsPanel.y;
panelInternal.isDragging = true;
}
onPositionChanged: mouse => {
if (panelInternal.isDragging && pressed) {
var currentPos = mapToItem(widgetsContainer, mouse.x, mouse.y);
var deltaX = currentPos.x - pressPos.x;
var deltaY = currentPos.y - pressPos.y;
var newX = panelInternal.baseX + deltaX;
var newY = panelInternal.baseY + deltaY;
// Boundary clamping
newX = Math.max(0, Math.min(newX, widgetsContainer.width - editModeControlsPanel.width));
newY = Math.max(0, Math.min(newY, widgetsContainer.height - editModeControlsPanel.height));
panelInternal.dragOffsetX = newX;
panelInternal.dragOffsetY = newY;
}
}
onReleased: {
if (panelInternal.isDragging) {
panelInternal.baseX = panelInternal.dragOffsetX;
panelInternal.baseY = panelInternal.dragOffsetY;
panelInternal.isDragging = false;
}
}
onCanceled: {
panelInternal.isDragging = false;
}
}
anchors {
top: editModeControlsBackground.top
right: editModeControlsBackground.right
topMargin: Style.marginXL
rightMargin: Style.marginXL
}
text: I18n.tr("settings.desktop-widgets.edit-mode.exit-button")
icon: "logout"
//backgroundColor: Color.mSurface
//textColor: Color.mOnSurface
//hoverColor: Color.mSurfaceVariant
outlined: false
fontSize: Style.fontSizeM * 1.1
iconSize: Style.fontSizeL * 1.1
z: 10000
onClicked: Settings.data.desktopWidgets.editMode = false
}
ColumnLayout {
id: controlsLayout
anchors {
fill: parent
margins: Style.marginXL
}
spacing: Style.marginL
// Controls explanation text
NText {
id: controlsExplanation
visible: Settings.data.desktopWidgets.editMode && Settings.data.desktopWidgets.enabled
anchors {
top: editModeButton.bottom
right: editModeControlsBackground.right
topMargin: Style.marginXL
rightMargin: Style.marginXL
}
text: I18n.tr("settings.desktop-widgets.edit-mode.controls-explanation")
pointSize: Style.fontSizeS
color: Color.mOnSurfaceVariant
horizontalAlignment: Text.AlignRight
wrapMode: Text.WordWrap
width: Math.min(implicitWidth, 300 * Style.uiScaleRatio)
z: 10000
}
RowLayout {
Layout.alignment: Qt.AlignRight
spacing: Style.marginS
// Grid snap checkbox
RowLayout {
id: gridSnapCheckbox
visible: Settings.data.desktopWidgets.editMode && Settings.data.desktopWidgets.enabled
anchors {
top: controlsExplanation.bottom
right: editModeControlsBackground.right
topMargin: Style.marginXL
rightMargin: Style.marginXL
}
spacing: Style.marginS
z: 10000
NIconButton {
id: addWidgetButton
icon: "layout-grid-add"
tooltipText: I18n.tr("settings.desktop-widgets.edit-mode.add-widget")
onClicked: {
var popupMenuWindow = PanelService.getPopupMenuWindow(window.screen);
if (popupMenuWindow) {
// Build menu items from registry
var items = [];
var widgets = DesktopWidgetRegistry.widgets;
for (var id in widgets) {
items.push({
action: id,
text: DesktopWidgetRegistry.getWidgetDisplayName(id),
icon: "layout-grid-add"
});
}
var globalPos = addWidgetButton.mapToItem(null, 0, addWidgetButton.height + Style.marginS);
popupMenuWindow.showDynamicContextMenu(items, globalPos.x, globalPos.y, function (widgetId) {
addWidgetToCurrentScreen(widgetId);
return false;
});
}
}
}
NText {
text: I18n.tr("settings.desktop-widgets.edit-mode.grid-snap.label")
pointSize: Style.fontSizeS
color: Color.mOnSurfaceVariant
horizontalAlignment: Text.AlignRight
}
NIconButton {
icon: "grid-4x4"
tooltipText: I18n.tr("settings.desktop-widgets.edit-mode.grid-snap.label")
colorBg: Settings.data.desktopWidgets.gridSnap ? Color.mPrimary : Color.mSurfaceVariant
colorFg: Settings.data.desktopWidgets.gridSnap ? Color.mOnPrimary : Color.mPrimary
onClicked: Settings.data.desktopWidgets.gridSnap = !Settings.data.desktopWidgets.gridSnap
}
NCheckbox {
checked: Settings.data.desktopWidgets.gridSnap
onToggled: checked => Settings.data.desktopWidgets.gridSnap = checked
NIconButton {
icon: "settings"
tooltipText: I18n.tr("settings.desktop-widgets.edit-mode.open-settings")
onClicked: {
if (Settings.data.ui.settingsPanelMode === "window") {
SettingsPanelService.toggleWindow(SettingsPanel.Tab.DesktopWidgets);
} else {
var settingsPanel = PanelService.getPanel("settingsPanel", screenLoader.modelData);
if (settingsPanel) {
settingsPanel.requestedTab = SettingsPanel.Tab.DesktopWidgets;
settingsPanel.toggle();
}
}
}
}
NButton {
text: I18n.tr("settings.desktop-widgets.edit-mode.exit-button")
icon: "logout"
outlined: false
fontSize: Style.fontSizeS
iconSize: Style.fontSizeM
onClicked: DesktopWidgetRegistry.editMode = false
}
}
NText {
Layout.alignment: Qt.AlignRight
Layout.maximumWidth: 300 * Style.uiScaleRatio
text: I18n.tr("settings.desktop-widgets.edit-mode.controls-explanation")
pointSize: Style.fontSizeS
color: Color.mOnSurfaceVariant
horizontalAlignment: Text.AlignRight
wrapMode: Text.WordWrap
}
}
}
}
+353 -63
View File
@@ -2,6 +2,9 @@ import QtQuick
import QtQuick.Effects
import Quickshell
import qs.Commons
import qs.Services.Noctalia
import qs.Services.UI
import qs.Widgets
Item {
id: root
@@ -26,6 +29,7 @@ Item {
readonly property real scaleSensitivity: 0.0015
readonly property real scaleUpdateThreshold: 0.015
readonly property real cornerScaleSensitivity: 0.0003 // Much lower sensitivity for corner handles
// Grid size ensures lines pass through screen center on both axes
readonly property int gridSize: {
@@ -99,6 +103,8 @@ Item {
property real lastScale: 1.0
// Locks operation type to prevent switching between drag/scale mid-operation
property string operationType: "" // "drag" or "scale" or ""
property real previousWidth: 0
property real centerX: baseX + (previousWidth > 0 ? previousWidth / 2 : root.width / 2)
}
function snapToGrid(coord) {
@@ -131,6 +137,105 @@ Item {
}
}
function removeWidget() {
if (widgetIndex < 0 || !screen || !screen.name) {
return;
}
var monitorWidgets = Settings.data.desktopWidgets.monitorWidgets || [];
var newMonitorWidgets = monitorWidgets.slice();
for (var i = 0; i < newMonitorWidgets.length; i++) {
if (newMonitorWidgets[i].name === screen.name) {
var widgets = (newMonitorWidgets[i].widgets || []).slice();
if (widgetIndex >= 0 && widgetIndex < widgets.length) {
widgets.splice(widgetIndex, 1);
newMonitorWidgets[i] = Object.assign({}, newMonitorWidgets[i], {
"widgets": widgets
});
Settings.data.desktopWidgets.monitorWidgets = newMonitorWidgets;
}
break;
}
}
}
function openWidgetSettings() {
if (!widgetData || !widgetData.id || !screen) {
return;
}
var widgetId = widgetData.id;
var hasSettings = false;
// Check if widget has settings
if (DesktopWidgetRegistry.isPluginWidget(widgetId)) {
var pluginId = widgetId.replace("plugin:", "");
var manifest = PluginRegistry.getPluginManifest(pluginId);
if (manifest && manifest.entryPoints && manifest.entryPoints.settings) {
hasSettings = true;
}
} else {
hasSettings = DesktopWidgetRegistry.widgetSettingsMap[widgetId] !== undefined;
}
if (!hasSettings) {
Logger.w("DraggableDesktopWidget", "Widget does not have settings:", widgetId);
return;
}
var popupMenuWindow = PanelService.getPopupMenuWindow(screen);
if (!popupMenuWindow) {
Logger.e("DraggableDesktopWidget", "No popup menu window found for screen");
return;
}
// Hide the dynamic context menu (popup window stays open for the dialog)
if (popupMenuWindow.hideDynamicMenu) {
popupMenuWindow.hideDynamicMenu();
}
var component = Qt.createComponent(Quickshell.shellDir + "/Modules/Panels/Settings/DesktopWidgets/DesktopWidgetSettingsDialog.qml");
function instantiateAndOpen() {
var dialog = component.createObject(popupMenuWindow.dialogParent, {
"widgetIndex": widgetIndex,
"widgetData": widgetData,
"widgetId": widgetId,
"sectionId": screen.name
});
if (dialog) {
dialog.updateWidgetSettings.connect((sec, idx, settings) => {
root.updateWidgetData(settings);
});
popupMenuWindow.hasDialog = true;
dialog.closed.connect(() => {
popupMenuWindow.hasDialog = false;
popupMenuWindow.close();
dialog.destroy();
});
dialog.open();
} else {
Logger.e("DraggableDesktopWidget", "Failed to create widget settings dialog");
}
}
if (component.status === Component.Ready) {
instantiateAndOpen();
} else if (component.status === Component.Error) {
Logger.e("DraggableDesktopWidget", "Error loading settings dialog component:", component.errorString());
} else {
component.statusChanged.connect(() => {
if (component.status === Component.Ready) {
instantiateAndOpen();
} else if (component.status === Component.Error) {
Logger.e("DraggableDesktopWidget", "Error loading settings dialog component:", component.errorString());
}
});
}
}
x: internal.isDragging ? internal.dragOffsetX : internal.baseX
y: internal.isDragging ? internal.dragOffsetY : internal.baseY
@@ -138,6 +243,22 @@ Item {
scale: widgetScale
transformOrigin: Item.TopLeft
// Adjust position when width changes to maintain center position
onWidthChanged: {
if (!internal.isDragging && !internal.isScaling && internal.previousWidth > 0 && width > 0) {
var widthDelta = width - internal.previousWidth;
// Adjust baseX to keep center position constant
internal.baseX = internal.baseX - widthDelta / 2;
internal.centerX = internal.baseX + width / 2;
}
internal.previousWidth = width;
}
Component.onCompleted: {
internal.previousWidth = width;
internal.centerX = internal.baseX + width / 2;
}
onWidgetDataChanged: {
if (!internal.isDragging) {
internal.baseX = (widgetData && widgetData.x !== undefined) ? widgetData.x : defaultX;
@@ -145,6 +266,9 @@ Item {
if (widgetData && widgetData.scale !== undefined) {
widgetScale = widgetData.scale;
}
// Update centerX and previousWidth when widget data changes
internal.previousWidth = width;
internal.centerX = internal.baseX + width / 2;
}
}
@@ -152,10 +276,10 @@ Item {
id: decorationRect
anchors.fill: parent
anchors.margins: -Style.marginS
color: Settings.data.desktopWidgets.editMode ? Qt.rgba(Color.mPrimary.r, Color.mPrimary.g, Color.mPrimary.b, 0.1) : Color.transparent
border.color: (Settings.data.desktopWidgets.editMode || internal.isDragging) ? (internal.isDragging ? Color.mOutline : Color.mPrimary) : Color.transparent
border.width: Settings.data.desktopWidgets.editMode ? 3 : (internal.isDragging ? 2 : 0)
radius: Style.radiusL + Style.marginS
color: DesktopWidgetRegistry.editMode ? Qt.rgba(Color.mPrimary.r, Color.mPrimary.g, Color.mPrimary.b, 0.1) : Color.transparent
border.color: (DesktopWidgetRegistry.editMode || internal.isDragging) ? (internal.isDragging ? Color.mOutline : Color.mPrimary) : Color.transparent
border.width: DesktopWidgetRegistry.editMode ? 3 : 0
radius: Style.radiusL
z: -1
}
@@ -189,26 +313,59 @@ Item {
z: 1
}
// Drag and Scale MouseArea - handles both dragging (left-click) and scaling (right-click)
// Context menu model and handler - menu is created dynamically in PopupMenuWindow
property var contextMenuModel: {
var hasSettings = false;
if (widgetData && widgetData.id) {
var widgetId = widgetData.id;
if (DesktopWidgetRegistry.isPluginWidget(widgetId)) {
var pluginId = widgetId.replace("plugin:", "");
var manifest = PluginRegistry.getPluginManifest(pluginId);
hasSettings = manifest && manifest.entryPoints && manifest.entryPoints.settings;
} else {
hasSettings = DesktopWidgetRegistry.widgetSettingsMap[widgetId] !== undefined;
}
}
var items = [];
if (hasSettings) {
items.push({
"label": I18n.tr("context-menu.widget-settings"),
"action": "widget-settings",
"icon": "settings"
});
}
items.push({
"label": I18n.tr("context-menu.delete"),
"action": "delete",
"icon": "trash"
});
return items;
}
function handleContextMenuAction(action) {
if (action === "widget-settings") {
// Don't close - openWidgetSettings will use the popup window for the dialog
root.openWidgetSettings();
return true; // Signal that we're handling close ourselves
} else if (action === "delete") {
root.removeWidget();
return false; // Let caller close the popup
}
return false;
}
// Drag MouseArea - handles dragging (left-click)
MouseArea {
id: interactionArea
id: dragArea
anchors.fill: parent
z: 1000
visible: Settings.data.desktopWidgets.editMode
cursorShape: {
if (internal.isDragging)
return Qt.ClosedHandCursor;
if (internal.isScaling)
return Qt.SizeAllCursor;
// Change cursor based on which button user is likely to press
// Right mouse button for scaling, left for dragging
return Qt.OpenHandCursor;
}
visible: DesktopWidgetRegistry.editMode
cursorShape: internal.isDragging ? Qt.ClosedHandCursor : Qt.OpenHandCursor
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
acceptedButtons: Qt.LeftButton
property point pressPos: Qt.point(0, 0)
property real initialScale: 1.0
onPressed: mouse => {
// Prevent starting new operation if one is already in progress
@@ -217,21 +374,10 @@ Item {
}
pressPos = Qt.point(mouse.x, mouse.y);
if (mouse.button === Qt.LeftButton) {
internal.operationType = "drag";
internal.dragOffsetX = root.x;
internal.dragOffsetY = root.y;
internal.isDragging = true;
} else if (mouse.button === Qt.RightButton) {
internal.operationType = "scale";
internal.isScaling = true;
internal.initialWidth = root.width;
internal.initialHeight = root.height;
internal.initialMousePos = Qt.point(mouse.x, mouse.y);
internal.initialScale = root.widgetScale;
internal.lastScale = root.widgetScale;
}
internal.operationType = "drag";
internal.dragOffsetX = root.x;
internal.dragOffsetY = root.y;
internal.isDragging = true;
}
onPositionChanged: mouse => {
@@ -242,7 +388,6 @@ Item {
var deltaX = globalCurrentPos.x - globalPressPos.x;
var deltaY = globalCurrentPos.y - globalPressPos.y;
// Calculate new position based on original position when drag started
var newX = internal.dragOffsetX + deltaX;
var newY = internal.dragOffsetY + deltaY;
@@ -266,24 +411,6 @@ Item {
internal.dragOffsetX = newX;
internal.dragOffsetY = newY;
} else if (internal.isScaling && pressed && internal.operationType === "scale") {
var dx = mouse.x - internal.initialMousePos.x;
var dy = mouse.y - internal.initialMousePos.y;
// Use primary direction of movement to determine scale change
var primaryMovement = (Math.abs(dx) > Math.abs(dy)) ? dx : dy;
// Scale change relative to initial widget size ensures consistent behavior
var scaleChange = primaryMovement * root.scaleSensitivity;
// Add to last applied scale (not initial) to allow smooth continuous scaling
var newScale = Math.max(minScale, Math.min(maxScale, internal.lastScale + scaleChange));
// Apply smoothing threshold to prevent rapid changes
if (Math.abs(root.widgetScale - newScale) > root.scaleUpdateThreshold && !isNaN(newScale) && newScale > 0) {
root.widgetScale = newScale;
internal.lastScale = newScale;
}
}
}
@@ -296,25 +423,188 @@ Item {
internal.baseX = internal.dragOffsetX;
internal.baseY = internal.dragOffsetY;
internal.centerX = internal.baseX + root.width / 2;
internal.isDragging = false;
internal.operationType = "";
} else if (internal.isScaling && internal.operationType === "scale") {
root.updateWidgetData({
"scale": root.widgetScale
});
internal.isScaling = false;
internal.operationType = "";
internal.lastScale = root.widgetScale;
}
}
onCanceled: {
internal.isDragging = false;
internal.isScaling = false;
internal.operationType = "";
// Sync lastScale when operation is canceled to prevent drift
internal.lastScale = root.widgetScale;
}
}
// Right-click MouseArea for context menu
MouseArea {
id: contextMenuArea
anchors.fill: parent
z: 1001
visible: DesktopWidgetRegistry.editMode
acceptedButtons: Qt.RightButton
hoverEnabled: true
onPressed: mouse => {
if (mouse.button === Qt.RightButton) {
var popupMenuWindow = PanelService.getPopupMenuWindow(root.screen);
if (popupMenuWindow) {
// Map click position to screen coordinates
var globalPos = root.mapToItem(null, mouse.x, mouse.y);
// Use dynamic context menu (created in PopupMenuWindow's Top layer)
// This ensures input events work correctly for desktop widgets (Bottom layer)
popupMenuWindow.showDynamicContextMenu(root.contextMenuModel, globalPos.x, globalPos.y, root.handleContextMenuAction);
}
}
}
}
// Corner handles for scaling - using Repeater to avoid code duplication
readonly property real cornerHandleSize: 8
readonly property real outlineMargin: Style.marginS
readonly property color colorHandle: Color.mSecondary
// Corner handle model: defines position, opposite corner, cursor, and triangle points for each corner
// xMult/yMult: multipliers for position (0 = left/top edge, 1 = right/bottom edge)
// oppXMult/oppYMult: multipliers for opposite corner calculation
// cursor: resize cursor type (FDiag for TL-BR diagonal, BDiag for TR-BL diagonal)
// points: triangle vertices as [x, y] pairs normalized to cornerHandleSize
readonly property var cornerHandleModel: [
{
xMult: 0,
yMult: 0,
oppXMult: 1,
oppYMult: 1,
cursor: Qt.SizeFDiagCursor,
points: [[0, 0], [1, 0], [0, 1]]
},
{
xMult: 1,
yMult: 0,
oppXMult: 0,
oppYMult: 1,
cursor: Qt.SizeBDiagCursor,
points: [[1, 0], [1, 1], [0, 0]]
},
{
xMult: 0,
yMult: 1,
oppXMult: 1,
oppYMult: 0,
cursor: Qt.SizeBDiagCursor,
points: [[0, 1], [0, 0], [1, 1]]
},
{
xMult: 1,
yMult: 1,
oppXMult: 0,
oppYMult: 0,
cursor: Qt.SizeFDiagCursor,
points: [[1, 1], [1, 0], [0, 1]]
}
]
Repeater {
model: root.cornerHandleModel
delegate: Canvas {
id: cornerHandle
required property var modelData
required property int index
visible: DesktopWidgetRegistry.editMode && !internal.isDragging
x: modelData.xMult * (root.width + outlineMargin) - (modelData.xMult === 0 ? outlineMargin : cornerHandleSize)
y: modelData.yMult * (root.height + outlineMargin) - (modelData.yMult === 0 ? outlineMargin : cornerHandleSize)
width: cornerHandleSize
height: cornerHandleSize
z: 2000
onPaint: {
var ctx = getContext("2d");
ctx.reset();
ctx.fillStyle = colorHandle;
ctx.beginPath();
ctx.moveTo(modelData.points[0][0] * cornerHandleSize, modelData.points[0][1] * cornerHandleSize);
ctx.lineTo(modelData.points[1][0] * cornerHandleSize, modelData.points[1][1] * cornerHandleSize);
ctx.lineTo(modelData.points[2][0] * cornerHandleSize, modelData.points[2][1] * cornerHandleSize);
ctx.closePath();
ctx.fill();
}
Component.onCompleted: requestPaint()
onVisibleChanged: if (visible)
requestPaint()
Connections {
target: root
function onWidthChanged() {
if (cornerHandle.visible)
cornerHandle.requestPaint();
}
function onHeightChanged() {
if (cornerHandle.visible)
cornerHandle.requestPaint();
}
}
MouseArea {
id: scaleMouseArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton
cursorShape: cornerHandle.modelData.cursor
property point pressPos: Qt.point(0, 0)
// Capture opposite corner at press time to avoid feedback loop during scaling
property real oppositeCornerX: 0
property real oppositeCornerY: 0
property real initialDistance: 0
onPressed: mouse => {
if (internal.operationType !== "") {
return;
}
pressPos = mapToItem(root.parent, mouse.x, mouse.y);
// Calculate and store opposite corner position using initial scale
oppositeCornerX = root.x + cornerHandle.modelData.oppXMult * root.width * root.widgetScale;
oppositeCornerY = root.y + cornerHandle.modelData.oppYMult * root.height * root.widgetScale;
initialDistance = Math.sqrt(Math.pow(pressPos.x - oppositeCornerX, 2) + Math.pow(pressPos.y - oppositeCornerY, 2));
internal.operationType = "scale";
internal.isScaling = true;
internal.initialScale = root.widgetScale;
internal.lastScale = root.widgetScale;
}
onPositionChanged: mouse => {
if (internal.isScaling && pressed && internal.operationType === "scale" && initialDistance > 0) {
var currentPos = mapToItem(root.parent, mouse.x, mouse.y);
// Use the fixed opposite corner position captured at press time
var currentDistance = Math.sqrt(Math.pow(currentPos.x - oppositeCornerX, 2) + Math.pow(currentPos.y - oppositeCornerY, 2));
var scaleRatio = currentDistance / initialDistance;
var newScale = Math.max(minScale, Math.min(maxScale, internal.initialScale * scaleRatio));
if (!isNaN(newScale) && newScale > 0) {
root.widgetScale = newScale;
internal.lastScale = newScale;
}
}
}
onReleased: mouse => {
if (internal.isScaling && internal.operationType === "scale") {
root.updateWidgetData({
"scale": root.widgetScale
});
internal.isScaling = false;
internal.operationType = "";
internal.lastScale = root.widgetScale;
}
}
onCanceled: {
internal.isScaling = false;
internal.operationType = "";
internal.lastScale = root.widgetScale;
}
}
}
}
}
@@ -5,6 +5,7 @@ import Quickshell
import qs.Commons
import qs.Modules.DesktopWidgets
import qs.Services.Media
import qs.Services.UI
import qs.Widgets
import qs.Widgets.AudioSpectrum
@@ -15,18 +16,40 @@ DraggableDesktopWidget {
// Widget settings
readonly property string hideMode: (widgetData.hideMode !== undefined) ? widgetData.hideMode : "visible"
readonly property bool showButtons: (widgetData.showButtons !== undefined) ? widgetData.showButtons : true
readonly property bool hasPlayer: MediaService.currentPlayer !== null
readonly property bool isPlaying: MediaService.isPlaying
// State
readonly property bool shouldHideIdle: (hideMode === "idle") && !isPlaying
readonly property bool shouldHideEmpty: !hasPlayer && hideMode === "hidden"
readonly property bool isHidden: (shouldHideIdle || shouldHideEmpty) && !Settings.data.desktopWidgets.editMode
readonly property bool isHidden: (shouldHideIdle || shouldHideEmpty) && !DesktopWidgetRegistry.editMode
visible: !isHidden
// CavaService registration for visualizer
readonly property string cavaComponentId: "desktopmediaplayer:" + (root.screen ? root.screen.name : "unknown")
onShouldShowVisualizerChanged: {
if (root.shouldShowVisualizer) {
CavaService.registerComponent(root.cavaComponentId);
} else {
CavaService.unregisterComponent(root.cavaComponentId);
}
}
Component.onCompleted: {
if (root.shouldShowVisualizer) {
CavaService.registerComponent(root.cavaComponentId);
}
}
Component.onDestruction: {
CavaService.unregisterComponent(root.cavaComponentId);
}
readonly property bool showPrev: hasPlayer && MediaService.canGoPrevious
readonly property bool showNext: hasPlayer && MediaService.canGoNext
readonly property int visibleButtonCount: 1 + (showPrev ? 1 : 0) + (showNext ? 1 : 0)
readonly property int visibleButtonCount: root.showButtons ? (1 + (showPrev ? 1 : 0) + (showNext ? 1 : 0)) : 0
readonly property int baseWidth: 400 * Style.uiScaleRatio
readonly property int buttonWidth: 32 * Style.uiScaleRatio
readonly property int buttonSpacing: Style.marginXS
@@ -192,6 +215,7 @@ DraggableDesktopWidget {
id: controlsRow
spacing: Style.marginXS
z: 10
visible: root.showButtons
NIconButton {
visible: showPrev
+19
View File
@@ -24,6 +24,25 @@ Loader {
id: root
active: false
// Track if the visualizer should be shown (lockscreen active + media playing + non-compact mode)
readonly property bool needsCava: root.active && !Settings.data.general.compactLockScreen && Settings.data.audio.visualizerType !== "" && Settings.data.audio.visualizerType !== "none"
onActiveChanged: {
if (root.active && root.needsCava) {
CavaService.registerComponent("lockscreen");
} else {
CavaService.unregisterComponent("lockscreen");
}
}
onNeedsCavaChanged: {
if (root.needsCava) {
CavaService.registerComponent("lockscreen");
} else {
CavaService.unregisterComponent("lockscreen");
}
}
Component.onCompleted: {
// Register with panel service
PanelService.lockScreen = this;
+56
View File
@@ -3,6 +3,7 @@ import Quickshell
import Quickshell.Wayland
import qs.Commons
import qs.Services.UI
import qs.Widgets
// Generic full-screen popup window for menus and context menus
// This is a top-level PanelWindow (sibling to MainScreen, not nested inside it)
@@ -20,6 +21,9 @@ PanelWindow {
// Expose the trayMenu Loader directly (for backward compatibility)
readonly property alias trayMenuLoader: trayMenuLoader
// Dynamic context menu callback for items in other windows (e.g., desktop widgets)
property var dynamicMenuCallback: null
anchors.top: true
anchors.left: true
anchors.right: true
@@ -55,6 +59,26 @@ PanelWindow {
}
}
// Dynamic context menu - created as child of this window (Top layer) so input works correctly
// Used for items in other windows like desktop widgets (bottom layer)
NPopupContextMenu {
id: dynamicMenu
visible: false
screen: root.screen
onTriggered: (action, item) => {
if (root.dynamicMenuCallback) {
// Callback returns true if it will handle closing (e.g., opening a dialog)
var handled = root.dynamicMenuCallback(action);
if (!handled) {
root.close();
}
} else {
root.close();
}
}
}
function open() {
visible = true;
}
@@ -67,6 +91,35 @@ PanelWindow {
}
}
// Show a dynamic context menu with model and callback at screen coordinates
// Used for items in other window layers (e.g., desktop widgets in bottom layer)
function showDynamicContextMenu(model, screenX, screenY, callback) {
dynamicMenu.model = model;
dynamicMenuCallback = callback;
// Use the anchor point item for positioning at absolute coordinates
dynamicMenu.anchorItem = anchorPoint;
anchorPoint.x = screenX;
anchorPoint.y = screenY;
contentItem = dynamicMenu;
dynamicMenu.visible = true;
open();
}
// Invisible anchor point for dynamic menu positioning
Item {
id: anchorPoint
width: 1
height: 1
}
// Hide just the dynamic menu without closing the popup window
// Used when transitioning from context menu to a dialog
function hideDynamicMenu() {
dynamicMenu.visible = false;
}
function close() {
visible = false;
// Call close/hide method on current content
@@ -77,6 +130,9 @@ PanelWindow {
contentItem.close();
}
}
// Hide dynamic menu
dynamicMenu.visible = false;
dynamicMenuCallback = null;
// Restore TrayMenu as default content
if (trayMenuLoader.item) {
contentItem = trayMenuLoader.item;
+34
View File
@@ -917,6 +917,23 @@ SmartPanel {
active: visible
sourceComponent: Component {
Loader {
anchors.fill: parent
sourceComponent: Settings.data.appLauncher.iconMode === "tabler" && modelData.isTablerIcon ? tablerIconComponent : systemIconComponent
}
}
Component {
id: tablerIconComponent
NIcon {
icon: modelData.icon
pointSize: Style.fontSizeXXXL
visible: modelData.icon && !modelData.emojiChar
}
}
Component {
id: systemIconComponent
IconImage {
anchors.fill: parent
source: modelData.icon ? ThemeIcons.iconFromName(modelData.icon, "application-x-executable") : ""
@@ -1268,6 +1285,23 @@ SmartPanel {
active: visible
sourceComponent: Component {
Loader {
anchors.fill: parent
sourceComponent: Settings.data.appLauncher.iconMode === "tabler" && modelData.isTablerIcon ? gridTablerIconComponent : gridSystemIconComponent
}
}
Component {
id: gridTablerIconComponent
NIcon {
icon: modelData.icon
pointSize: Style.fontSizeXXXL
visible: modelData.icon && !modelData.emojiChar
}
}
Component {
id: gridSystemIconComponent
IconImage {
anchors.fill: parent
source: modelData.icon ? ThemeIcons.iconFromName(modelData.icon, "application-x-executable") : ""
@@ -5,6 +5,7 @@ import qs.Commons
Item {
property var launcher: null
property string name: I18n.tr("plugins.calculator")
property string iconMode: Settings.data.appLauncher.iconMode
function handleCommand(query) {
// Handle >calc command or direct math expressions after >
@@ -16,7 +17,8 @@ Item {
{
"name": ">calc",
"description": I18n.tr("plugins.calculator-description"),
"icon": "accessories-calculator",
"icon": iconMode === "tabler" ? "calculator" : "accessories-calculator",
"isTablerIcon": true,
"isImage": false,
"onActivate": function () {
launcher.setSearchText(">calc ");
@@ -41,7 +43,8 @@ Item {
{
"name": I18n.tr("plugins.calculator-name"),
"description": I18n.tr("plugins.calculator-enter-expression"),
"icon": "accessories-calculator",
"icon": iconMode === "tabler" ? "calculator" : "accessories-calculator",
"isTablerIcon": true,
"isImage": false,
"onActivate": function () {}
}
@@ -55,7 +58,8 @@ Item {
{
"name": AdvancedMath.formatResult(result),
"description": `${expression} = ${result}`,
"icon": "accessories-calculator",
"icon": iconMode === "tabler" ? "calculator" : "accessories-calculator",
"isTablerIcon": true,
"isImage": false,
"onActivate": function () {
// TODO: copy entry to clipboard via ClipHist
@@ -68,7 +72,8 @@ Item {
{
"name": I18n.tr("plugins.calculator-error"),
"description": error.message || "Invalid expression",
"icon": "dialog-error",
"icon": iconMode === "tabler" ? "circle-x" : "dialog-error",
"isTablerIcon": true,
"isImage": false,
"onActivate": function () {}
}
@@ -9,6 +9,7 @@ Item {
// Plugin metadata
property string name: I18n.tr("plugins.clipboard")
property var launcher: null
property string iconMode: Settings.data.appLauncher.iconMode
// Plugin capabilities
property bool handleSearch: false // Don't handle regular search
@@ -70,7 +71,8 @@ Item {
{
"name": ">clip",
"description": I18n.tr("plugins.clipboard-search-description"),
"icon": "text-x-generic",
"icon": iconMode === "tabler" ? "clipboard" : "text-x-generic",
"isTablerIcon": true,
"isImage": false,
"onActivate": function () {
launcher.setSearchText(">clip ");
@@ -79,7 +81,8 @@ Item {
{
"name": ">clip clear",
"description": I18n.tr("plugins.clipboard-clear-description"),
"icon": "text-x-generic",
"icon": iconMode === "tabler" ? "trash" : "delete_sweep",
"isTablerIcon": true,
"isImage": false,
"onActivate": function () {
ClipboardService.wipeAll();
@@ -105,7 +108,8 @@ Item {
{
"name": I18n.tr("plugins.clipboard-history-disabled"),
"description": I18n.tr("plugins.clipboard-history-disabled-description"),
"icon": "view-refresh",
"icon": iconMode === "tabler" ? "refresh" : "view-refresh",
"isTablerIcon": true,
"isImage": false,
"onActivate": function () {}
}
@@ -118,7 +122,8 @@ Item {
{
"name": I18n.tr("plugins.clipboard-clear-history"),
"description": I18n.tr("plugins.clipboard-clear-description-full"),
"icon": "delete_sweep",
"icon": iconMode === "tabler" ? "trash" : "delete_sweep",
"isTablerIcon": true,
"isImage": false,
"onActivate": function () {
ClipboardService.wipeAll();
@@ -134,7 +139,8 @@ Item {
{
"name": I18n.tr("plugins.clipboard-loading"),
"description": I18n.tr("plugins.clipboard-loading-description"),
"icon": "view-refresh",
"icon": iconMode === "tabler" ? "refresh" : "view-refresh",
"isTablerIcon": true,
"isImage": false,
"onActivate": function () {}
}
@@ -152,7 +158,8 @@ Item {
{
"name": I18n.tr("plugins.clipboard-loading"),
"description": I18n.tr("plugins.clipboard-loading-description"),
"icon": "view-refresh",
"icon": iconMode === "tabler" ? "refresh" : "view-refresh",
"isTablerIcon": true,
"isImage": false,
"onActivate": function () {}
}
@@ -193,7 +200,8 @@ Item {
results.push({
"name": searchTerm ? "No matching clipboard items" : "Clipboard is empty",
"description": searchTerm ? `No items containing "${query}"` : "Copy something to see it here",
"icon": "text-x-generic",
"icon": iconMode === "tabler" ? "clipboard" : "text-x-generic",
"isTablerIcon": true,
"isImage": false,
"onActivate": function () {// Do nothing
}
@@ -210,7 +218,8 @@ Item {
return {
"name": meta ? `Image ${meta.w}×${meta.h}` : "Image",
"description": meta ? `${meta.fmt} ${meta.size}` : item.mime || "Image data",
"icon": "image",
"icon": iconMode === "tabler" ? "photo" : "image",
"isTablerIcon": true,
"isImage": true,
"imageWidth": meta ? meta.w : 0,
"imageHeight": meta ? meta.h : 0,
@@ -244,7 +253,8 @@ Item {
return {
"name": title,
"description": description,
"icon": "text-x-generic",
"icon": iconMode === "tabler" ? "clipboard" : "text-x-generic",
"isTablerIcon": true,
"isImage": false,
"clipboardId": item.id,
"preview": preview
@@ -5,6 +5,7 @@ import qs.Commons
Item {
property var launcher: null
property string name: I18n.tr("plugins.command")
property string iconMode: Settings.data.appLauncher.iconMode
function handleCommand(query) {
return query.startsWith(">cmd");
@@ -15,7 +16,8 @@ Item {
{
"name": ">cmd",
"description": I18n.tr("plugins.command-description"),
"icon": "utilities-terminal",
"icon": iconMode === "tabler" ? "terminal" : "utilities-terminal",
"isTablerIcon": true,
"isImage": false,
"onActivate": function () {
launcher.setSearchText(">cmd ");
@@ -33,7 +35,8 @@ Item {
{
"name": I18n.tr("plugins.command-name"),
"description": I18n.tr("plugins.command-description"),
"icon": "utilities-terminal",
"icon": iconMode === "tabler" ? "terminal" : "utilities-terminal",
"isTablerIcon": true,
"isImage": false,
"onActivate": function () {
launcher.close();
@@ -9,6 +9,7 @@ Item {
// Plugin metadata
property string name: I18n.tr("plugins.emoji")
property var launcher: null
property string iconMode: Settings.data.appLauncher.iconMode
property bool handleSearch: false
property string selectedCategory: "recent"
@@ -67,7 +68,8 @@ Item {
{
"name": ">emoji",
"description": I18n.tr("plugins.emoji-search-description"),
"icon": "face-smile",
"icon": iconMode === "tabler" ? "mood-smile" : "face-smile",
"isTablerIcon": true,
"isImage": false,
"onActivate": function () {
launcher.setSearchText(">emoji ");
@@ -87,7 +89,8 @@ Item {
{
"name": I18n.tr("plugins.emoji-loading"),
"description": I18n.tr("plugins.emoji-loading-description"),
"icon": "view-refresh",
"icon": iconMode === "tabler" ? "refresh" : "view-refresh",
"isTablerIcon": true,
"isImage": false,
"onActivate": function () {}
}
+1 -24
View File
@@ -64,34 +64,11 @@ SmartPanel {
id: pluginContentItem
anchors.fill: parent
// Plugin content loader, pluginApi is injected synchronously in loadPluginPanel()
Loader {
id: pluginContentLoader
anchors.fill: parent
active: false
// Create a dummy pluginApi that returns empty strings to avoid undefined warnings
property var _dummyApi: QtObject {
property var pluginSettings: ({})
property var manifest: ({
metadata: {
defaultSettings: {}
}
})
function tr(key) {
return "";
}
function trp(key, count) {
return "";
}
}
onLoaded: {
// Inject the dummy API immediately to prevent undefined warnings
if (item && item.hasOwnProperty("pluginApi") && !item.pluginApi) {
item.pluginApi = _dummyApi;
}
}
}
}
@@ -15,6 +15,7 @@ ColumnLayout {
property string valueVisualizerType: widgetData.visualizerType !== undefined ? widgetData.visualizerType : widgetMetadata.visualizerType
property string valueHideMode: widgetData.hideMode !== undefined ? widgetData.hideMode : widgetMetadata.hideMode
property string valueVisualizerVisibility: widgetData.visualizerVisibility !== undefined ? widgetData.visualizerVisibility : (widgetMetadata.visualizerVisibility !== undefined ? widgetMetadata.visualizerVisibility : "always")
property bool valueShowButtons: widgetData.showButtons !== undefined ? widgetData.showButtons : (widgetMetadata.showButtons !== undefined ? widgetMetadata.showButtons : true)
function saveSettings() {
var settings = Object.assign({}, widgetData || {});
@@ -22,6 +23,7 @@ ColumnLayout {
settings.visualizerType = valueVisualizerType;
settings.hideMode = valueHideMode;
settings.visualizerVisibility = valueVisualizerVisibility;
settings.showButtons = valueShowButtons;
return settings;
}
@@ -33,6 +35,14 @@ ColumnLayout {
onToggled: checked => valueShowBackground = checked
}
NToggle {
Layout.fillWidth: true
label: I18n.tr("settings.desktop-widgets.media-player.show-buttons.label")
description: I18n.tr("settings.desktop-widgets.media-player.show-buttons.description")
checked: valueShowButtons
onToggled: checked => valueShowButtons = checked
}
NComboBox {
Layout.fillWidth: true
label: I18n.tr("settings.desktop-widgets.media-player.visualizer-type.label")
-19
View File
@@ -383,25 +383,6 @@ ColumnLayout {
onSelected: key => Settings.data.audio.visualizerType = key
}
NComboBox {
label: I18n.tr("settings.audio.media.visualizer-quality.label")
description: I18n.tr("settings.audio.media.visualizer-quality.description")
model: [
{
"key": "low",
"name": I18n.tr("options.visualizer-quality.low")
},
{
"key": "high",
"name": I18n.tr("options.visualizer-quality.high")
}
]
currentKey: Settings.data.audio.visualizerQuality
isSettings: true
defaultValue: Settings.getDefaultValue("audio.visualizerQuality")
onSelected: key => Settings.data.audio.visualizerQuality = key
}
NComboBox {
label: I18n.tr("settings.audio.media.frame-rate.label")
description: I18n.tr("settings.audio.media.frame-rate.description")
@@ -45,11 +45,11 @@ ColumnLayout {
NButton {
visible: Settings.data.desktopWidgets.enabled
Layout.fillWidth: true
text: Settings.data.desktopWidgets.editMode ? I18n.tr("settings.desktop-widgets.edit-mode.exit-button") : I18n.tr("settings.desktop-widgets.edit-mode.button.label")
text: DesktopWidgetRegistry.editMode ? I18n.tr("settings.desktop-widgets.edit-mode.exit-button") : I18n.tr("settings.desktop-widgets.edit-mode.button.label")
icon: "edit"
onClicked: {
Settings.data.desktopWidgets.editMode = !Settings.data.desktopWidgets.editMode;
if (Settings.data.desktopWidgets.editMode && Settings.data.ui.settingsPanelMode !== "window") {
DesktopWidgetRegistry.editMode = !DesktopWidgetRegistry.editMode;
if (DesktopWidgetRegistry.editMode && Settings.data.ui.settingsPanelMode !== "window") {
var item = root.parent;
while (item) {
if (item.closeRequested !== undefined) {
@@ -93,6 +93,12 @@ ColumnLayout {
onToggled: checked => Settings.data.appLauncher.sortByMostUsed = checked
}
NToggle {
label: I18n.tr("settings.launcher.settings.icon-mode.label")
description: I18n.tr("settings.launcher.settings.icon-mode.description")
checked: Settings.data.appLauncher.iconMode === "native"
onToggled: checked => Settings.data.appLauncher.iconMode = checked ? "native" : "tabler"
}
NToggle {
label: I18n.tr("settings.launcher.settings.use-app2unit.label")
description: I18n.tr("settings.launcher.settings.use-app2unit.description")
+5 -1
View File
@@ -4,6 +4,7 @@ import QtQuick.Layouts
import Quickshell
import qs.Commons
import qs.Services.Networking
import qs.Services.System
import qs.Widgets
ColumnLayout {
@@ -16,12 +17,15 @@ ColumnLayout {
NToggle {
label: I18n.tr("settings.network.wifi.label")
checked: Settings.data.network.wifiEnabled
description: I18n.tr("settings.network.wifi.description")
checked: ProgramCheckerService.nmcliAvailable && Settings.data.network.wifiEnabled
onToggled: checked => NetworkService.setWifiEnabled(checked)
enabled: ProgramCheckerService.nmcliAvailable
}
NToggle {
label: I18n.tr("settings.network.bluetooth.label")
description: I18n.tr("settings.network.bluetooth.description")
checked: BluetoothService.enabled
onToggled: checked => BluetoothService.setBluetoothEnabled(checked)
}
@@ -363,11 +363,11 @@ ColumnLayout {
Layout.preferredWidth: 30 * Style.uiScaleRatio
Layout.preferredHeight: 30 * Style.uiScaleRatio
NIconButton {
icon: "restore"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.notifications.duration.reset")
onClicked: Settings.data.notifications.lowUrgencyDuration = 3
NIconButton {
icon: "restore"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.notifications.duration.reset")
onClicked: Settings.data.notifications.lowUrgencyDuration = 3
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
@@ -398,11 +398,11 @@ ColumnLayout {
Layout.preferredWidth: 30 * Style.uiScaleRatio
Layout.preferredHeight: 30 * Style.uiScaleRatio
NIconButton {
icon: "restore"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.notifications.duration.reset")
onClicked: Settings.data.notifications.normalUrgencyDuration = 8
NIconButton {
icon: "restore"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.notifications.duration.reset")
onClicked: Settings.data.notifications.normalUrgencyDuration = 8
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
@@ -432,11 +432,11 @@ ColumnLayout {
Layout.preferredWidth: 30 * Style.uiScaleRatio
Layout.preferredHeight: 30 * Style.uiScaleRatio
NIconButton {
icon: "restore"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.notifications.duration.reset")
onClicked: Settings.data.notifications.criticalUrgencyDuration = 15
NIconButton {
icon: "restore"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.notifications.duration.reset")
onClicked: Settings.data.notifications.criticalUrgencyDuration = 15
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
+151 -109
View File
@@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "../../../../Helpers/FuzzySort.js" as Fuzzysort
import qs.Commons
import qs.Services.Noctalia
import qs.Services.UI
@@ -98,9 +99,10 @@ ColumnLayout {
for (var i = 0; i < allIds.length; i++) {
var manifest = PluginRegistry.getPluginManifest(allIds[i]);
if (manifest) {
// Create a copy of manifest and include update info
// Create a copy of manifest and include update info and enabled state
var pluginData = JSON.parse(JSON.stringify(manifest));
pluginData._updateInfo = PluginService.pluginUpdates[allIds[i]];
pluginData._enabled = PluginRegistry.isPluginEnabled(allIds[i]);
plugins.push(pluginData);
}
}
@@ -208,6 +210,16 @@ ColumnLayout {
}
}
NIconButton {
icon: "trash"
tooltipText: I18n.tr("settings.plugins.uninstall")
baseSize: Style.baseWidgetSize * 0.7
onClicked: {
uninstallDialog.pluginToUninstall = modelData;
uninstallDialog.open();
}
}
NButton {
id: updateButton
property string pluginId: modelData.id
@@ -251,7 +263,7 @@ ColumnLayout {
}
NToggle {
checked: PluginRegistry.isPluginEnabled(modelData.id)
checked: modelData._enabled
baseSize: Style.baseWidgetSize * 0.7
onToggled: function (checked) {
if (checked) {
@@ -378,6 +390,7 @@ ColumnLayout {
RowLayout {
spacing: Style.marginM
Layout.fillWidth: true
Layout.topMargin: Style.marginM
Layout.bottomMargin: Style.marginM
NTabBar {
@@ -428,25 +441,18 @@ ColumnLayout {
}
}
// Timer to check for updates after refresh starts
Timer {
id: checkUpdatesTimer
interval: 100
onTriggered: {
PluginService.checkForUpdates();
}
}
// Timer to recheck updates after available plugins are updated
Timer {
id: recheckUpdatesTimer
interval: 50
onTriggered: {
PluginService.checkForUpdates();
}
}
property string pluginFilter: "all"
property string pluginSearchText: ""
// Search input
NTextInput {
placeholderText: I18n.tr("placeholders.search")
inputIconName: "search"
text: root.pluginSearchText
onTextChanged: root.pluginSearchText = text
Layout.fillWidth: true
Layout.bottomMargin: Style.marginL
}
// Available plugins list
ColumnLayout {
@@ -460,6 +466,7 @@ ColumnLayout {
var all = PluginService.availablePlugins || [];
var filtered = [];
// First apply download filter
for (var i = 0; i < all.length; i++) {
var plugin = all[i];
var downloaded = plugin.downloaded || false;
@@ -473,106 +480,135 @@ ColumnLayout {
}
}
// Then apply fuzzy search if there's search text
var query = root.pluginSearchText.trim();
if (query !== "") {
var results = Fuzzysort.go(query, filtered, {
"keys": ["name", "description"],
"threshold": 0.35,
"limit": 50
});
filtered = [];
for (var j = 0; j < results.length; j++) {
filtered.push(results[j].obj);
}
} else {
// Sort by lastUpdated (most recent first) when not searching
filtered.sort(function (a, b) {
var dateA = a.lastUpdated ? new Date(a.lastUpdated).getTime() : 0;
var dateB = b.lastUpdated ? new Date(b.lastUpdated).getTime() : 0;
return dateB - dateA;
});
}
return filtered;
}
delegate: NBox {
id: pluginBox
property bool isHovered: hoverHandler.hovered
Layout.fillWidth: true
Layout.leftMargin: Style.borderS
Layout.rightMargin: Style.borderS
implicitHeight: Math.round(contentRow.implicitHeight + Style.marginL * 2)
implicitHeight: Math.round(contentColumn.implicitHeight + Style.marginL * 2)
color: Color.mSurface
RowLayout {
id: contentRow
Behavior on implicitHeight {
NumberAnimation {
duration: 150
easing.type: Easing.OutCubic
}
}
HoverHandler {
id: hoverHandler
}
ColumnLayout {
id: contentColumn
anchors.fill: parent
anchors.margins: Style.marginL
spacing: Style.marginM
spacing: Style.marginS
NIcon {
icon: "plugin"
pointSize: Style.fontSizeXL
color: Color.mOnSurface
}
ColumnLayout {
spacing: 2
RowLayout {
spacing: Style.marginM
Layout.fillWidth: true
NIcon {
icon: "plugin"
pointSize: Style.fontSizeL
color: Color.mOnSurface
}
NText {
text: modelData.name
font.weight: Font.Medium
color: Color.mOnSurface
elide: Text.ElideRight
Layout.fillWidth: true
}
// Downloaded indicator
NIcon {
icon: "circle-check"
pointSize: Style.fontSizeL
color: Color.mPrimary
visible: modelData.downloaded === true
}
// Install/Uninstall button
NIconButton {
icon: modelData.downloaded ? "trash" : "download"
baseSize: Style.baseWidgetSize * 0.7
tooltipText: modelData.downloaded ? I18n.tr("settings.plugins.uninstall") : I18n.tr("settings.plugins.install")
onClicked: {
if (modelData.downloaded) {
uninstallDialog.pluginToUninstall = modelData;
uninstallDialog.open();
} else {
installPlugin(modelData);
}
}
}
}
// Description - visible on hover
NText {
visible: pluginBox.isHovered && modelData.description
text: modelData.description || ""
font.pointSize: Style.fontSizeXS
color: Color.mOnSurface
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
// Details row - visible on hover
RowLayout {
visible: pluginBox.isHovered
spacing: Style.marginS
Layout.fillWidth: true
NText {
text: modelData.description
text: "v" + modelData.version
font.pointSize: Style.fontSizeXS
color: Color.mOnSurfaceVariant
wrapMode: Text.WordWrap
maximumLineCount: 2
elide: Text.ElideRight
}
NText {
text: "•"
font.pointSize: Style.fontSizeXS
color: Color.mOnSurfaceVariant
}
NText {
text: stripAuthorEmail(modelData.author)
font.pointSize: Style.fontSizeXS
color: Color.mOnSurfaceVariant
}
Item {
Layout.fillWidth: true
}
RowLayout {
spacing: Style.marginS
NText {
text: "v" + modelData.version
font.pointSize: Style.fontSizeXXS
color: Color.mOnSurfaceVariant
}
NText {
text: "•"
font.pointSize: Style.fontSizeXXS
color: Color.mOnSurfaceVariant
}
NText {
text: stripAuthorEmail(modelData.author)
font.pointSize: Style.fontSizeXXS
color: Color.mOnSurfaceVariant
}
NText {
text: "•"
font.pointSize: Style.fontSizeXXS
color: Color.mOnSurfaceVariant
}
NText {
text: modelData.source?.name || "Unknown"
font.pointSize: Style.fontSizeXS
color: Color.mOnSurfaceVariant
}
}
}
// Downloaded indicator
NIcon {
icon: "circle-check"
pointSize: Style.fontSizeXL
color: Color.mPrimary
visible: modelData.downloaded === true
}
// Install/Uninstall button
NIconButton {
icon: modelData.downloaded ? "trash" : "download"
baseSize: Style.baseWidgetSize * 0.7
tooltipText: modelData.downloaded ? I18n.tr("settings.plugins.uninstall") : I18n.tr("settings.plugins.install")
onClicked: {
if (modelData.downloaded) {
uninstallDialog.pluginToUninstall = modelData;
uninstallDialog.open();
} else {
installPlugin(modelData);
}
}
}
}
}
@@ -599,6 +635,7 @@ ColumnLayout {
// Add source dialog
Popup {
id: addSourceDialog
parent: Overlay.overlay
modal: true
dim: false
anchors.centerIn: parent
@@ -672,6 +709,7 @@ ColumnLayout {
// Uninstall confirmation dialog
Popup {
id: uninstallDialog
parent: Overlay.overlay
modal: true
dim: false
anchors.centerIn: parent
@@ -733,6 +771,23 @@ ColumnLayout {
showToastOnSave: true
}
// Timer to check for updates after refresh starts
Timer {
id: checkUpdatesTimer
interval: 100
onTriggered: {
PluginService.checkForUpdates();
}
}
// Timer to recheck updates after available plugins are updated
Timer {
id: recheckUpdatesTimer
interval: 50
onTriggered: {
PluginService.checkForUpdates();
}
}
// ------------------------------
// Functions
// ------------------------------
@@ -783,21 +838,8 @@ ColumnLayout {
target: PluginRegistry
function onPluginsChanged() {
// Force model refresh for installed plugins
installedPluginsRepeater.model = undefined;
Qt.callLater(function () {
installedPluginsRepeater.model = Qt.binding(function () {
var allIds = PluginRegistry.getAllInstalledPluginIds();
var plugins = [];
for (var i = 0; i < allIds.length; i++) {
var manifest = PluginRegistry.getPluginManifest(allIds[i]);
if (manifest) {
plugins.push(manifest);
}
}
return plugins;
});
});
// Force model refresh for installed plugins by incrementing counter
root.installedPluginsRefreshCounter++;
// Force model refresh for plugin sources
pluginSourcesRepeater.model = undefined;
+5 -1
View File
@@ -8,7 +8,11 @@
<p align="center">
<a href="https://docs.noctalia.dev/getting-started/installation">
<img src="https://img.shields.io/badge/⚡_QUICK_INSTALL-Get_Started_Now-A8AEFF?style=for-the-badge&logoColor=FFFFFF&labelColor=0C0D11" alt="Quick Install" style="height: 50px" />
<img
src="https://img.shields.io/badge/🌙_Install_Noctalia-A8AEFF?style=for-the-badge&labelColor=0C0D11"
alt="Install Noctalia"
style="height: 50px"
/>
</a>
</p>
+3
View File
@@ -489,5 +489,8 @@ Item {
function enable() {
Settings.data.desktopWidgets.enabled = true;
}
function edit() {
DesktopWidgetRegistry.editMode = !DesktopWidgetRegistry.editMode;
}
}
}
+24 -25
View File
@@ -9,31 +9,30 @@ import qs.Services.UI
Singleton {
id: root
/**
* Cava runs if:
* - Bar has an audio visualizer
* - LockScreen is opened
* - A control center is open
* - Desktop media player has a visualizer enabled
*/
readonly property bool hasDesktopMediaVisualizer: (function () {
var monitorWidgets = Settings.data.desktopWidgets.monitorWidgets;
if (!monitorWidgets)
return false;
for (var i = 0; i < monitorWidgets.length; i++) {
var widgets = monitorWidgets[i].widgets;
if (!widgets)
continue;
for (var j = 0; j < widgets.length; j++) {
var widget = widgets[j];
if (widget.id === "MediaPlayer" && widget.visualizerType && widget.visualizerType !== "" && widget.visualizerType !== "none") {
return true;
}
}
}
return false;
})()
property bool shouldRun: BarService.hasAudioVisualizer || PanelService.lockScreen?.active || (PanelService.openedPanel && PanelService.openedPanel.objectName.startsWith("controlCenterPanel")) || hasDesktopMediaVisualizer
// Register a component that needs audio data, call this when a visualizer becomes active.
// Pass a unique identifier (e.g., "lockscreen", "controlcenter:screen1", "plugin:fancy-audiovisualizer")
function registerComponent(componentId) {
root.registeredComponents[componentId] = true;
root.registeredComponents = Object.assign({}, root.registeredComponents);
Logger.d("Cava", "Component registered:", componentId, "- total:", root.registeredCount);
}
// Unregister a component when it no longer needs audio data.
function unregisterComponent(componentId) {
delete root.registeredComponents[componentId];
root.registeredComponents = Object.assign({}, root.registeredComponents);
Logger.d("Cava", "Component unregistered:", componentId, "- total:", root.registeredCount);
}
// Check if a component is registered
function isRegistered(componentId) {
return root.registeredComponents[componentId] === true;
}
// Component registration - any component needing audio data registers here
property var registeredComponents: ({})
readonly property int registeredCount: Object.keys(registeredComponents).length
property bool shouldRun: registeredCount > 0
property var values: []
property int barsCount: 32
+31 -9
View File
@@ -4,6 +4,7 @@ import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
import qs.Services.System
import qs.Services.UI
Singleton {
@@ -68,8 +69,21 @@ Singleton {
Component.onCompleted: {
Logger.i("Network", "Service started");
syncWifiState();
scan();
if (ProgramCheckerService.nmcliAvailable) {
syncWifiState();
scan();
}
}
// Start initial checks when nmcli becomes available
Connections {
target: ProgramCheckerService
function onNmcliAvailableChanged() {
if (ProgramCheckerService.nmcliAvailable) {
syncWifiState();
scan();
}
}
}
// Save cache with debounce
@@ -91,37 +105,41 @@ Singleton {
}
// Ethernet check timer
// Always running every 30s
// Runs every 30s if nmcli is available
Timer {
id: ethernetCheckTimer
interval: 30000
running: true
running: ProgramCheckerService.nmcliAvailable
repeat: true
onTriggered: ethernetStateProcess.running = true
}
// Internet connectivity check timer
// Always running every 15s
// Runs every 15s if nmcli is available
Timer {
id: connectivityCheckTimer
interval: 15000
running: true
running: ProgramCheckerService.nmcliAvailable
repeat: true
onTriggered: connectivityCheckProcess.running = true
}
// Core functions
function syncWifiState() {
if (!ProgramCheckerService.nmcliAvailable)
return;
wifiStateProcess.running = true;
}
function setWifiEnabled(enabled) {
if (!ProgramCheckerService.nmcliAvailable)
return;
Settings.data.network.wifiEnabled = enabled;
wifiStateEnableProcess.running = true;
}
function scan() {
if (!Settings.data.network.wifiEnabled)
if (!ProgramCheckerService.nmcliAvailable || !Settings.data.network.wifiEnabled)
return;
if (scanning) {
// Mark current scan results to be ignored and schedule a new scan
@@ -141,7 +159,7 @@ Singleton {
}
function connect(ssid, password = "") {
if (connecting)
if (!ProgramCheckerService.nmcliAvailable || connecting)
return;
connecting = true;
connectingTo = ssid;
@@ -162,12 +180,16 @@ Singleton {
}
function disconnect(ssid) {
if (!ProgramCheckerService.nmcliAvailable)
return;
disconnectingFrom = ssid;
disconnectProcess.ssid = ssid;
disconnectProcess.running = true;
}
function forget(ssid) {
if (!ProgramCheckerService.nmcliAvailable)
return;
forgettingNetwork = ssid;
// Remove from cache
@@ -239,7 +261,7 @@ Singleton {
// Processes
Process {
id: ethernetStateProcess
running: true
running: ProgramCheckerService.nmcliAvailable
command: ["nmcli", "-t", "-f", "DEVICE,TYPE,STATE", "device"]
stdout: StdioCollector {
+136 -94
View File
@@ -115,6 +115,9 @@ Singleton {
}
}
// Track pending plugin loads for init completion
property int _pendingPluginLoads: 0
function init() {
if (root.initialized) {
Logger.d("PluginService", "Already initialized, skipping");
@@ -133,12 +136,12 @@ Singleton {
var enabledIds = PluginRegistry.getEnabledPluginIds();
Logger.i("PluginService", "Found", enabledIds.length, "enabled plugins:", JSON.stringify(enabledIds));
// Count plugins that need to be loaded
var pluginsToLoad = [];
for (var i = 0; i < enabledIds.length; i++) {
Logger.d("PluginService", "Attempting to load plugin:", enabledIds[i]);
var manifest = PluginRegistry.getPluginManifest(enabledIds[i]);
if (manifest) {
Logger.d("PluginService", "Manifest found for", enabledIds[i]);
loadPlugin(enabledIds[i]);
pluginsToLoad.push(enabledIds[i]);
} else {
Logger.w("PluginService", "Plugin", enabledIds[i], "is enabled but not found on disk - cleaning up");
// Plugin was deleted from disk but still marked as enabled
@@ -149,13 +152,38 @@ Singleton {
}
}
// Mark plugins as fully loaded
root.pluginsFullyLoaded = true;
Logger.i("PluginService", "All plugins loaded");
root.allPluginsLoaded();
// If no plugins to load, mark as complete immediately
if (pluginsToLoad.length === 0) {
root.pluginsFullyLoaded = true;
Logger.i("PluginService", "No plugins to load");
root.allPluginsLoaded();
refreshAvailablePlugins();
return;
}
// Fetch available plugins from all sources
refreshAvailablePlugins();
// Track pending loads
root._pendingPluginLoads = pluginsToLoad.length;
// Load all plugins (async - they will call _onPluginLoadComplete when done)
for (var j = 0; j < pluginsToLoad.length; j++) {
Logger.d("PluginService", "Attempting to load plugin:", pluginsToLoad[j]);
loadPlugin(pluginsToLoad[j]);
}
}
// Called when a plugin finishes loading (success or failure)
function _onPluginLoadComplete() {
root._pendingPluginLoads--;
if (root._pendingPluginLoads <= 0) {
// All plugins finished loading
root.pluginsFullyLoaded = true;
Logger.i("PluginService", "All plugins loaded");
root.allPluginsLoaded();
// Fetch available plugins from all sources
refreshAvailablePlugins();
}
}
// Refresh available plugins from all sources
@@ -223,13 +251,20 @@ Singleton {
}
Logger.i("PluginService", "Loaded", registry.plugins.length, "plugins from", source.name);
// Remove from active fetches BEFORE emitting signal so handler sees correct count
delete activeFetches[source.url];
fetchProcess.destroy();
root.availablePluginsUpdated();
return;
}
} catch (e) {
Logger.e("PluginService", "Failed to parse registry from", source.name, ":", e);
Logger.e("PluginService", "Response was:", response ? response.substring(0, 200) : "null");
}
// Clean up on error or empty response
delete activeFetches[source.url];
fetchProcess.destroy();
});
@@ -487,6 +522,19 @@ Singleton {
return changed;
}
// Load plugin settings and translations before instantiating components
// This ensures pluginApi is fully populated before being passed to createObject()
function loadPluginData(pluginId, manifest, callback) {
// Load settings first
loadPluginSettings(pluginId, function (settings) {
// Then load translations
loadPluginTranslationsAsync(pluginId, manifest, I18n.langCode, function (translations) {
// Both ready - call back with complete data
callback(settings, translations);
});
});
}
// Load a plugin
function loadPlugin(pluginId) {
if (root.loadedPlugins[pluginId]) {
@@ -504,97 +552,98 @@ Singleton {
Logger.i("PluginService", "Loading plugin:", pluginId);
// Create plugin API object
var pluginApi = createPluginAPI(pluginId, manifest);
// Load settings and translations FIRST, then create API and instantiate components
loadPluginData(pluginId, manifest, function (settings, translations) {
// Create plugin API object with pre-loaded data
var pluginApi = createPluginAPI(pluginId, manifest, settings, translations);
// Initialize plugin entry with API and manifest
root.loadedPlugins[pluginId] = {
barWidget: null,
desktopWidget: null,
mainInstance: null,
api: pluginApi,
manifest: manifest
};
// Initialize plugin entry with API and manifest
root.loadedPlugins[pluginId] = {
barWidget: null,
desktopWidget: null,
mainInstance: null,
api: pluginApi,
manifest: manifest
};
// Clear any previous errors for this plugin
root.clearPluginError(pluginId);
// Clear any previous errors for this plugin
root.clearPluginError(pluginId);
// Load Main.qml entry point if it exists
if (manifest.entryPoints && manifest.entryPoints.main) {
var mainPath = pluginDir + "/" + manifest.entryPoints.main;
var loadVersion = PluginRegistry.pluginLoadVersions[pluginId] || 0;
var mainComponent = Qt.createComponent("file://" + mainPath + "?v=" + loadVersion);
// Load Main.qml entry point if it exists
if (manifest.entryPoints && manifest.entryPoints.main) {
var mainPath = pluginDir + "/" + manifest.entryPoints.main;
var loadVersion = PluginRegistry.pluginLoadVersions[pluginId] || 0;
var mainComponent = Qt.createComponent("file://" + mainPath + "?v=" + loadVersion);
if (mainComponent.status === Component.Ready) {
// Get the plugin container from shell.qml (must be in graphics scene)
if (!root.pluginContainer) {
Logger.e("PluginService", "Plugin container not set. Shell must set PluginService.pluginContainer.");
return;
}
// Instantiate Main.qml with container as parent (places it in graphics scene)
var mainInstance = mainComponent.createObject(root.pluginContainer);
if (mainInstance) {
// Set pluginApi property after creation
if (mainInstance.hasOwnProperty('pluginApi')) {
mainInstance.pluginApi = pluginApi;
} else {
Logger.w("PluginService", "Main.qml for", pluginId, "should declare 'property var pluginApi: null'");
if (mainComponent.status === Component.Ready) {
// Get the plugin container from shell.qml (must be in graphics scene)
if (!root.pluginContainer) {
Logger.e("PluginService", "Plugin container not set. Shell must set PluginService.pluginContainer.");
return;
}
root.loadedPlugins[pluginId].mainInstance = mainInstance;
pluginApi.mainInstance = mainInstance;
Logger.i("PluginService", "Loaded Main.qml for plugin:", pluginId);
} else {
root.recordPluginError(pluginId, "main", "Failed to instantiate Main.qml");
// Instantiate Main.qml with pluginApi passed directly in createObject
var mainInstance = mainComponent.createObject(root.pluginContainer, {
pluginApi: pluginApi
});
if (mainInstance) {
root.loadedPlugins[pluginId].mainInstance = mainInstance;
pluginApi.mainInstance = mainInstance;
Logger.i("PluginService", "Loaded Main.qml for plugin:", pluginId);
} else {
root.recordPluginError(pluginId, "main", "Failed to instantiate Main.qml");
}
} else if (mainComponent.status === Component.Error) {
root.recordPluginError(pluginId, "main", mainComponent.errorString());
}
} else if (mainComponent.status === Component.Error) {
root.recordPluginError(pluginId, "main", mainComponent.errorString());
}
}
// Load bar widget component if provided (don't instantiate - BarWidgetRegistry will do that)
if (manifest.entryPoints && manifest.entryPoints.barWidget) {
var widgetPath = pluginDir + "/" + manifest.entryPoints.barWidget;
var widgetLoadVersion = PluginRegistry.pluginLoadVersions[pluginId] || 0;
var widgetComponent = Qt.createComponent("file://" + widgetPath + "?v=" + widgetLoadVersion);
// Load bar widget component if provided (don't instantiate - BarWidgetRegistry will do that)
if (manifest.entryPoints && manifest.entryPoints.barWidget) {
var widgetPath = pluginDir + "/" + manifest.entryPoints.barWidget;
var widgetLoadVersion = PluginRegistry.pluginLoadVersions[pluginId] || 0;
var widgetComponent = Qt.createComponent("file://" + widgetPath + "?v=" + widgetLoadVersion);
if (widgetComponent.status === Component.Ready) {
root.loadedPlugins[pluginId].barWidget = widgetComponent;
pluginApi.barWidget = widgetComponent;
if (widgetComponent.status === Component.Ready) {
root.loadedPlugins[pluginId].barWidget = widgetComponent;
pluginApi.barWidget = widgetComponent;
// Register with BarWidgetRegistry
BarWidgetRegistry.registerPluginWidget(pluginId, widgetComponent, manifest.metadata);
Logger.i("PluginService", "Loaded bar widget for plugin:", pluginId);
} else if (widgetComponent.status === Component.Error) {
root.recordPluginError(pluginId, "barWidget", widgetComponent.errorString());
// Register with BarWidgetRegistry
BarWidgetRegistry.registerPluginWidget(pluginId, widgetComponent, manifest.metadata);
Logger.i("PluginService", "Loaded bar widget for plugin:", pluginId);
} else if (widgetComponent.status === Component.Error) {
root.recordPluginError(pluginId, "barWidget", widgetComponent.errorString());
}
}
}
// Load desktop widget component if provided (don't instantiate - DesktopWidgetRegistry will do that)
if (manifest.entryPoints && manifest.entryPoints.desktopWidget) {
var desktopWidgetPath = pluginDir + "/" + manifest.entryPoints.desktopWidget;
var desktopWidgetLoadVersion = PluginRegistry.pluginLoadVersions[pluginId] || 0;
var desktopWidgetComponent = Qt.createComponent("file://" + desktopWidgetPath + "?v=" + desktopWidgetLoadVersion);
// Load desktop widget component if provided (don't instantiate - DesktopWidgetRegistry will do that)
if (manifest.entryPoints && manifest.entryPoints.desktopWidget) {
var desktopWidgetPath = pluginDir + "/" + manifest.entryPoints.desktopWidget;
var desktopWidgetLoadVersion = PluginRegistry.pluginLoadVersions[pluginId] || 0;
var desktopWidgetComponent = Qt.createComponent("file://" + desktopWidgetPath + "?v=" + desktopWidgetLoadVersion);
if (desktopWidgetComponent.status === Component.Ready) {
root.loadedPlugins[pluginId].desktopWidget = desktopWidgetComponent;
pluginApi.desktopWidget = desktopWidgetComponent;
if (desktopWidgetComponent.status === Component.Ready) {
root.loadedPlugins[pluginId].desktopWidget = desktopWidgetComponent;
pluginApi.desktopWidget = desktopWidgetComponent;
// Register with DesktopWidgetRegistry
DesktopWidgetRegistry.registerPluginWidget(pluginId, desktopWidgetComponent, manifest.metadata);
Logger.i("PluginService", "Loaded desktop widget for plugin:", pluginId);
} else if (desktopWidgetComponent.status === Component.Error) {
root.recordPluginError(pluginId, "desktopWidget", desktopWidgetComponent.errorString());
// Register with DesktopWidgetRegistry
DesktopWidgetRegistry.registerPluginWidget(pluginId, desktopWidgetComponent, manifest.metadata);
Logger.i("PluginService", "Loaded desktop widget for plugin:", pluginId);
} else if (desktopWidgetComponent.status === Component.Error) {
root.recordPluginError(pluginId, "desktopWidget", desktopWidgetComponent.errorString());
}
}
}
Logger.i("PluginService", "Plugin loaded:", pluginId);
root.pluginLoaded(pluginId);
Logger.i("PluginService", "Plugin loaded:", pluginId);
root.pluginLoaded(pluginId);
// Set up hot reload watcher if enabled
setupPluginFileWatcher(pluginId);
// Set up hot reload watcher if enabled
setupPluginFileWatcher(pluginId);
// Notify that this plugin finished loading (for init tracking)
root._onPluginLoadComplete();
});
}
// Unload a plugin
@@ -635,10 +684,9 @@ Singleton {
Logger.i("PluginService", "Unloaded plugin:", pluginId);
}
// Create plugin API object
function createPluginAPI(pluginId, manifest) {
// Create plugin API object with pre-loaded settings and translations
function createPluginAPI(pluginId, manifest, settings, translations) {
var pluginDir = PluginRegistry.getPluginDir(pluginId);
var settingsFile = PluginRegistry.getPluginSettingsFile(pluginId);
var api = Qt.createQmlObject(`
import QtQuick
@@ -679,15 +727,9 @@ Singleton {
// Set current language (can't use binding in Qt.createQmlObject string)
api.currentLanguage = I18n.langCode;
// Load plugin settings
loadPluginSettings(pluginId, function (settings) {
api.pluginSettings = settings;
});
// Load plugin translations for current language
loadPluginTranslationsAsync(pluginId, manifest, I18n.langCode, function (translations) {
api.pluginTranslations = translations;
});
// Set pre-loaded settings and translations (available immediately!)
api.pluginSettings = settings || {};
api.pluginTranslations = translations || {};
// ----------------------------------------
// Helper function to get nested property by dot notation
+3 -1
View File
@@ -34,6 +34,7 @@ Singleton {
property bool zedAvailable: false
property bool niriAvailable: false
property bool mangoAvailable: false
property bool nmcliAvailable: false
// Discord client auto-detection
property var availableDiscordClients: []
@@ -195,7 +196,8 @@ Singleton {
"zedAvailable": ["which", "zeditor"],
"emacsAvailable": ["sh", "-c", "test -d \"$HOME/.config/doom\" || test -d \"$HOME/.emacs.d\""],
"niriAvailable": ["which", "niri"],
"mangoAvailable": ["which", "mmsg"]
"mangoAvailable": ["which", "mmsg"],
"nmcliAvailable": ["which", "nmcli"]
})
// Internal tracking
+70 -2
View File
@@ -28,6 +28,8 @@ Singleton {
property var diskPercents: ({})
property real rxSpeed: 0
property real txSpeed: 0
property real zfsArcSizeKb: 0 // ZFS ARC cache size in KB
property real zfsArcCminKb: 0 // ZFS ARC minimum (non-reclaimable) size in KB
// Internal state for CPU calculation
property var prevCpuStats: null
@@ -66,6 +68,9 @@ Singleton {
// Kickoff the gpu sensor detection for temperature
gpuTempNameReader.checkNext();
// Check for ZFS ARC stats on startup
zfsArcStatsFile.reload();
}
// Re-run GPU detection when NVIDIA opt-in setting changes
@@ -134,7 +139,10 @@ Singleton {
restart();
}
}
onTriggered: memInfoFile.reload()
onTriggered: {
memInfoFile.reload();
zfsArcStatsFile.reload();
}
}
// Timer for disk usage
@@ -202,6 +210,19 @@ Singleton {
onLoaded: calculateNetworkSpeed(text())
}
// ZFS ARC stats file (only exists on ZFS systems)
FileView {
id: zfsArcStatsFile
path: "/proc/spl/kstat/zfs/arcstats"
printErrors: false
onLoaded: parseZfsArcStats(text())
onLoadFailed: {
// File doesn't exist (non-ZFS system), set ARC values to 0
root.zfsArcSizeKb = 0;
root.zfsArcCminKb = 0;
}
}
// --------------------------------------------
// Process to fetch disk usage in percent
// Uses 'df' aka 'disk free'
@@ -448,6 +469,49 @@ Singleton {
}
// -------------------------------------------------------
// -------------------------------------------------------
// Parse ZFS ARC stats from /proc/spl/kstat/zfs/arcstats
function parseZfsArcStats(text) {
if (!text)
return;
const lines = text.split('\n');
// The file format is: name type data
// We need to find the lines with "size" and "c_min" and extract the values (third column)
let foundSize = false;
let foundCmin = false;
for (const line of lines) {
const parts = line.trim().split(/\s+/);
if (parts.length >= 3) {
if (parts[0] === 'size') {
// The value is in bytes, convert to KB
const arcSizeBytes = parseInt(parts[2]) || 0;
root.zfsArcSizeKb = Math.floor(arcSizeBytes / 1024);
foundSize = true;
} else if (parts[0] === 'c_min') {
// The value is in bytes, convert to KB
const arcCminBytes = parseInt(parts[2]) || 0;
root.zfsArcCminKb = Math.floor(arcCminBytes / 1024);
foundCmin = true;
}
// If we found both, we can return early
if (foundSize && foundCmin) {
return;
}
}
}
// If fields not found, set to 0
if (!foundSize) {
root.zfsArcSizeKb = 0;
}
if (!foundCmin) {
root.zfsArcCminKb = 0;
}
}
// -------------------------------------------------------
// Parse memory info from /proc/meminfo
function parseMemoryInfo(text) {
@@ -466,7 +530,11 @@ Singleton {
}
if (memTotal > 0) {
const usageKb = memTotal - memAvailable;
// Calculate usage, adjusting for ZFS ARC cache if present
let usageKb = memTotal - memAvailable;
if (root.zfsArcSizeKb > 0) {
usageKb = Math.max(0, usageKb - root.zfsArcSizeKb + root.zfsArcCminKb);
}
root.memGb = (usageKb / 1048576).toFixed(1); // 1024*1024 = 1048576
root.memPercent = Math.round((usageKb / memTotal) * 100);
}
-30
View File
@@ -7,7 +7,6 @@ import qs.Commons
Singleton {
id: root
property bool hasAudioVisualizer: false
property bool isVisible: true
property var readyBars: ({})
@@ -18,33 +17,6 @@ Singleton {
signal activeWidgetsChanged
signal barReadyChanged(string screenName)
// onHasAudioVisualizerChanged: {
// Logger.d("BarService", "hasAudioVisualizer", hasAudioVisualizer)
// }
// Simple timer that run once when the widget structure has changed
// and determine if any MediaMini widget has the visualizer on
Timer {
id: timerCheckVisualizer
interval: 100
repeat: false
onTriggered: {
hasAudioVisualizer = false;
if (getAllWidgetInstances("AudioVisualizer").length > 0) {
hasAudioVisualizer = true;
return;
}
const widgets = getAllWidgetInstances("MediaMini");
for (var i = 0; i < widgets.length; i++) {
const widget = widgets[i];
if (widget.showVisualizer) {
hasAudioVisualizer = true;
return;
}
}
}
}
Component.onCompleted: {
Logger.i("BarService", "Service started");
}
@@ -75,8 +47,6 @@ Singleton {
"instance": instance
};
timerCheckVisualizer.restart();
Logger.d("BarService", "Registered widget:", key);
root.activeWidgetsChanged();
}
+17 -1
View File
@@ -4,10 +4,14 @@ import QtQuick
import Quickshell
import qs.Commons
import qs.Modules.DesktopWidgets.Widgets
import qs.Services.Noctalia
Singleton {
id: root
// Transient state - not persisted, resets on shell restart
property bool editMode: false
// Signal emitted when plugin widgets are registered/unregistered
signal pluginWidgetRegistryUpdated
@@ -62,7 +66,8 @@ Singleton {
"allowUserSettings": true,
"showBackground": true,
"visualizerType": "linear",
"hideMode": "visible"
"hideMode": "visible",
"showButtons": true
},
"Weather": {
"allowUserSettings": true,
@@ -110,6 +115,17 @@ Singleton {
return Object.keys(pluginWidgets);
}
// Get display name for a widget ID
function getWidgetDisplayName(widgetId) {
if (widgetId.startsWith("plugin:")) {
var pluginId = widgetId.replace("plugin:", "");
var manifest = PluginRegistry.getPluginManifest(pluginId);
return manifest ? manifest.name : pluginId;
}
// Core widgets - return as-is (Clock, MediaPlayer, Weather)
return widgetId;
}
// Register a plugin desktop widget
function registerPluginWidget(pluginId, component, metadata) {
if (!pluginId || !component) {
+2 -2
View File
@@ -30,7 +30,8 @@ RowLayout {
readonly property bool isValueChanged: isSettings && (currentKey !== defaultValue)
readonly property string indicatorTooltip: {
if (!isSettings) return "";
if (!isSettings)
return "";
var displayValue = "";
if (defaultValue === "") {
// Try to find the display name for empty key in the model
@@ -304,5 +305,4 @@ RowLayout {
}
}
}
}
+1 -29
View File
@@ -62,38 +62,10 @@ Popup {
color: Color.mOutline
}
// Settings loader
// Settings loader - pluginApi is passed via setSource() in openPluginSettings()
Loader {
id: settingsLoader
Layout.fillWidth: true
// Create a dummy pluginApi that returns empty strings to avoid undefined warnings
property var _dummyApi: QtObject {
property var pluginSettings: ({})
property var manifest: ({
metadata: {
defaultSettings: {}
}
})
function tr(key) {
return "";
}
function trp(key, count) {
return "";
}
function saveSettings() {
}
}
onLoaded: {
// Inject dummy API immediately to prevent undefined warnings during initialization
if (item && item.hasOwnProperty("pluginApi") && !item.pluginApi) {
item.pluginApi = _dummyApi;
}
}
}
// Action buttons
+16
View File
@@ -118,6 +118,22 @@ PopupWindow {
}
anchor.rect.y: {
if (anchorItem && screen) {
// Check if using absolute positioning (small anchor point item)
const isAbsolutePosition = anchorItem.width <= 1 && anchorItem.height <= 1;
if (isAbsolutePosition) {
// For absolute positioning, show menu directly at anchor Y
// Only adjust if menu would clip at bottom
const anchorGlobalPos = anchorItem.mapToItem(null, 0, 0);
const menuBottom = anchorGlobalPos.y + implicitHeight;
if (menuBottom > screen.height - Style.marginM) {
// Position above the click point instead
return -implicitHeight;
}
return 0;
}
const anchorCenterY = anchorItem.height / 2;
// Calculate base Y position based on bar orientation
+2 -2
View File
@@ -31,7 +31,8 @@ RowLayout {
readonly property bool isValueChanged: isSettings && (currentKey !== defaultValue)
readonly property string indicatorTooltip: {
if (!isSettings) return "";
if (!isSettings)
return "";
var displayValue = "";
if (defaultValue === "") {
// Try to find the display name for empty key in the model
@@ -387,5 +388,4 @@ RowLayout {
}
}
}
}
-1
View File
@@ -45,4 +45,3 @@ Rectangle {
}
}
}
+2 -3
View File
@@ -42,8 +42,8 @@ RowLayout {
readonly property bool isValueChanged: isSettings && (value !== defaultValue)
readonly property string indicatorTooltip: isSettings ? I18n.tr("settings.indicator.default-value", {
"value": String(defaultValue)
}) : ""
"value": String(defaultValue)
}) : ""
Timer {
id: repeatTimer
@@ -415,5 +415,4 @@ RowLayout {
}
}
}
}
+2 -3
View File
@@ -32,8 +32,8 @@ ColumnLayout {
readonly property bool isValueChanged: isSettings && (text !== defaultValue)
readonly property string indicatorTooltip: isSettings ? I18n.tr("settings.indicator.default-value", {
"value": defaultValue === "" ? "(empty)" : String(defaultValue)
}) : ""
"value": defaultValue === "" ? "(empty)" : String(defaultValue)
}) : ""
NLabel {
label: root.label
@@ -230,5 +230,4 @@ ColumnLayout {
}
}
}
}
+2 -3
View File
@@ -28,8 +28,8 @@ RowLayout {
readonly property bool isValueChanged: isSettings && (checked !== defaultValue)
readonly property string indicatorTooltip: isSettings ? I18n.tr("settings.indicator.default-value", {
"value": typeof defaultValue === "boolean" ? (defaultValue ? "true" : "false") : String(defaultValue)
}) : ""
"value": typeof defaultValue === "boolean" ? (defaultValue ? "true" : "false") : String(defaultValue)
}) : ""
NLabel {
label: root.label
@@ -107,5 +107,4 @@ RowLayout {
}
}
}
}
+4 -3
View File
@@ -32,14 +32,15 @@ ColumnLayout {
readonly property bool isValueChanged: isSettings && (value !== defaultValue)
readonly property string indicatorTooltip: {
if (!isSettings) return "";
if (!isSettings)
return "";
var defaultVal = defaultValue;
if (typeof defaultVal === "number") {
// If it's a decimal between 0 and 1, format as percentage
if (defaultVal > 0 && defaultVal <= 1 && from >= 0 && from < 1) {
return I18n.tr("settings.indicator.default-value", {
"value": Math.floor(defaultVal * 100) + "%"
});
"value": Math.floor(defaultVal * 100) + "%"
});
}
return I18n.tr("settings.indicator.default-value", {
"value": String(defaultVal)
-12
View File
@@ -143,18 +143,6 @@ ShellRoot {
PluginService.screenDetector = screenDetector;
}
}
// Listen for when available plugins are fetched, then check for updates
Connections {
target: PluginService
property bool hasCheckedOnStartup: false
function onAvailablePluginsUpdated() {
if (!hasCheckedOnStartup) {
hasCheckedOnStartup = true;
PluginService.checkForUpdates();
}
}
}
}
}