This commit is contained in:
2026-04-06 08:31:38 -05:00
parent aff58a1214
commit 5a84a8f294
41 changed files with 1211 additions and 104 deletions

View File

@@ -14,6 +14,8 @@ Item {
property ShellScreen screen
property string widgetId: ""
property string section: ""
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
// Bar positioning properties
readonly property string screenName: screen ? screen.name : ""

View File

@@ -10,7 +10,7 @@ Item {
property var pluginApi: null
// --- Logic extracted from BarWidget.qml ---
property bool micActive: false
property bool camActive: false
property bool scrActive: false
@@ -19,7 +19,7 @@ Item {
property var scrApps: []
property var accessHistory: []
// Previous states for history tracking
property var _prevMicApps: []
property var _prevCamApps: []
@@ -30,6 +30,7 @@ Item {
property var defaults: pluginApi?.manifest?.metadata?.defaultSettings || ({})
property bool enableToast: cfg.enableToast ?? defaults.enableToast ?? true
property string activeColorKey: cfg.activeColor ?? defaults.activeColor ?? "primary"
property string micFilterRegex: cfg.micFilterRegex ?? defaults.micFilterRegex ?? ""
PwObjectTracker {
objects: Pipewire.ready ? Pipewire.nodes.values : []
@@ -73,6 +74,16 @@ Item {
function updateMicrophoneState(nodes, links) {
var appNames = [];
var isActive = false;
var filterRegex = null;
if (root.micFilterRegex && root.micFilterRegex.length > 0) {
try {
filterRegex = new RegExp(root.micFilterRegex);
} catch (e) {
Logger.w("PrivacyIndicator: Invalid micFilterRegex:", root.micFilterRegex);
}
}
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
if (!node || !node.isStream || !node.audio || node.isSink) continue;
@@ -80,8 +91,11 @@ Item {
var mediaClass = node.properties["media.class"] || "";
if (mediaClass === "Stream/Input/Audio") {
if (node.properties["stream.capture.sink"] === "true") continue;
isActive = true;
var appName = getAppName(node);
if (filterRegex && appName && filterRegex.test(appName)) continue;
isActive = true;
if (appName && appNames.indexOf(appName) === -1) appNames.push(appName);
}
}
@@ -133,7 +147,7 @@ Item {
}
// --- History Persistence ---
property string stateFile: ""
property bool isLoaded: false
@@ -164,7 +178,7 @@ Item {
root.accessHistory = adapter.history;
}
}
onLoadFailed: error => {
// If file doesn't exist (error 2), we are ready to save new data
if (error === 2) {
@@ -178,9 +192,9 @@ Item {
function saveHistory() {
if (!stateFile || !isLoaded) return;
adapter.history = root.accessHistory;
// Ensure cache directory exists and save
try {
Quickshell.execDetached(["mkdir", "-p", Settings.cacheDir]);
@@ -220,7 +234,7 @@ Item {
function checkAppChanges(newApps, oldApps, type, icon, colorKey) {
if (!newApps && !oldApps) return;
// Check for new apps (Started)
if (newApps) {
for (var i = 0; i < newApps.length; i++) {
@@ -230,7 +244,7 @@ Item {
}
}
}
// Check for removed apps (Stopped)
if (oldApps) {
for (var j = 0; j < oldApps.length; j++) {
@@ -279,6 +293,6 @@ Item {
checkAppChanges(scrApps, _prevScrApps, "Screen", "screen-share", activeColorKey);
_prevScrApps = scrApps;
}
}

View File

@@ -18,8 +18,10 @@ A privacy indicator widget that monitors and displays when microphone, camera, o
Access the plugin settings in Noctalia to configure the following options:
- **Hide Inactive States**: If enabled, microphone, camera, and screen icons are hidden whenever they are inactive. Only active states are shown.
- **RemoveMargins**: If enabled, removes all outer margins of the widget.
- **Remove Margins**: If enabled, removes all outer margins of the widget.
- **Icon Spacing**: Controls the horizontal/vertical spacing between the icons.
- **Active/Inactive Icon Color**: Customize the colors for active and inactive states.
- **Microphone Filter Regex**: Regex pattern to filter out specific microphone applications. Matching apps are completely excluded from detection (they won't trigger the indicator or appear in tooltips). Use `|` to specify multiple patterns, e.g., `effect_input.rnnoise|easyeffects`.
## Usage

View File

@@ -17,6 +17,7 @@ ColumnLayout {
property int iconSpacing: cfg.iconSpacing ?? defaults.iconSpacing ?? 4
property string activeColor: cfg.activeColor ?? defaults.activeColor ?? "primary"
property string inactiveColor: cfg.inactiveColor ?? defaults.inactiveColor ?? "none"
property string micFilterRegex: cfg.micFilterRegex ?? defaults.micFilterRegex
spacing: Style.marginL
@@ -46,7 +47,7 @@ ColumnLayout {
onToggled: checked => {
root.enableToast = checked;
}
}
}
NToggle {
label: pluginApi?.tr("settings.removeMargins.label")
@@ -98,6 +99,15 @@ ColumnLayout {
currentKey: root.iconSpacing.toFixed(0)
onSelected: key => root.iconSpacing = key
}
NTextInput {
Layout.fillWidth: true
label: pluginApi?.tr("settings.micFilterRegex.label") || "Microphone filter regex"
description: pluginApi?.tr("settings.micFilterRegex.desc") || "Regex pattern to filter out microphone applications"
placeholderText: "effect_input.rnnoise|easyeffects"
text: root.micFilterRegex
onTextChanged: root.micFilterRegex = text
}
}
function saveSettings() {
@@ -112,6 +122,7 @@ ColumnLayout {
pluginApi.pluginSettings.removeMargins = root.removeMargins;
pluginApi.pluginSettings.activeColor = root.activeColor;
pluginApi.pluginSettings.inactiveColor = root.inactiveColor;
pluginApi.pluginSettings.micFilterRegex = root.micFilterRegex;
pluginApi.saveSettings();

View File

@@ -26,6 +26,10 @@
"removeMargins": {
"desc": "Remove all outer margins of the widget.",
"label": "Remove margins"
},
"micFilterRegex": {
"desc": "Regex pattern to filter out microphone applications. Matching apps are completely excluded from detection.",
"label": "Microphone filter regex"
}
},
"tooltip": {

View File

@@ -0,0 +1,54 @@
{
"menu": {
"settings": "Cài đặt tiện ích"
},
"settings": {
"activeColor": {
"desc": "Màu của biểu tượng khi đang hoạt động.",
"label": "Màu biểu tượng khi hoạt động"
},
"hideInactive": {
"desc": "Ẩn biểu tượng micro, camera và màn hình khi không hoạt động.",
"label": "Ẩn trạng thái không hoạt động"
},
"enableToast": {
"desc": "Hiển thị thông báo khi một trạng thái thay đổi.",
"label": "Bật thông báo"
},
"inactiveColor": {
"desc": "Màu của biểu tượng khi không hoạt động.",
"label": "Màu biểu tượng khi không hoạt động"
},
"iconSpacing": {
"desc": "Thiết lập khoảng cách giữa các biểu tượng.",
"label": "Khoảng cách biểu tượng"
},
"removeMargins": {
"desc": "Loại bỏ toàn bộ lề ngoài của widget.",
"label": "Xóa lề"
},
"micFilterRegex": {
"desc": "Biểu thức chính quy để lọc các ứng dụng sử dụng micro. Các ứng dụng khớp sẽ bị loại khỏi việc phát hiện.",
"label": "Regex lọc micro"
}
},
"tooltip": {
"cam-on": "Camera: {apps}",
"mic-on": "Micro: {apps}",
"screen-on": "Chia sẻ màn hình: {apps}"
},
"toast": {
"cam-on": "Camera đang hoạt động",
"mic-on": "Micro đang hoạt động",
"screen-on": "Chia sẻ màn hình đang hoạt động"
},
"history": {
"title": "Lịch sử truy cập",
"empty": "Không có truy cập gần đây",
"clear": "Xóa",
"action": {
"started": "Bắt đầu",
"stopped": "Dừng"
}
}
}

View File

@@ -1,7 +1,7 @@
{
"id": "privacy-indicator",
"name": "Privacy Indicator",
"version": "1.2.5",
"version": "1.2.7",
"minNoctaliaVersion": "3.6.0",
"author": "Noctalia Team",
"official": true,
@@ -29,7 +29,8 @@
"removeMargins": false,
"iconSpacing": 4,
"activeColor": "primary",
"inactiveColor": "none"
"inactiveColor": "none",
"micFilterRegex": ""
}
}
}
}