diff options
| author | seraphim <18ycm9tx@anonaddy.me> | 2026-07-24 20:57:33 +0700 |
|---|---|---|
| committer | seraphim <18ycm9tx@anonaddy.me> | 2026-07-24 20:57:33 +0700 |
| commit | 395c08d0a334cdffbcc370384bee67f19f964399 (patch) | |
| tree | d57cdf13385785aaf068d4f8408fe6cc2f095999 /noctalia/.config/plugins/privacy-indicator | |
frst
Diffstat (limited to 'noctalia/.config/plugins/privacy-indicator')
25 files changed, 1771 insertions, 0 deletions
diff --git a/noctalia/.config/plugins/privacy-indicator/BarWidget.qml b/noctalia/.config/plugins/privacy-indicator/BarWidget.qml new file mode 100644 index 0000000..bdebd41 --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/BarWidget.qml @@ -0,0 +1,175 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import qs.Commons +import qs.Services.UI +import qs.Widgets + +Item { + id: root + + property var pluginApi: null + + 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 : "" + readonly property string barPosition: Settings.getBarPositionForScreen(screenName) + readonly property bool isVertical: barPosition === "left" || barPosition === "right" + readonly property real barHeight: Style.getBarHeightForScreen(screenName) + readonly property real capsuleHeight: Style.getCapsuleHeightForScreen(screenName) + readonly property real barFontSize: Style.getBarFontSizeForScreen(screenName) + + // Access main instance for state + readonly property var mainInstance: pluginApi?.mainInstance + + property bool micActive: mainInstance ? mainInstance.micActive : false + property bool camActive: mainInstance ? mainInstance.camActive : false + property bool scrActive: mainInstance ? mainInstance.scrActive : false + property var micApps: mainInstance ? mainInstance.micApps : [] + property var camApps: mainInstance ? mainInstance.camApps : [] + property var scrApps: mainInstance ? mainInstance.scrApps : [] + + property var cfg: pluginApi?.pluginSettings || ({}) + property var defaults: pluginApi?.manifest?.metadata?.defaultSettings || ({}) + + property bool hideInactive: cfg.hideInactive ?? defaults.hideInactive ?? false + property bool enableToast: cfg.enableToast ?? defaults.enableToast ?? true + property bool removeMargins: cfg.removeMargins ?? defaults.removeMargins ?? false + property int iconSpacing: cfg.iconSpacing ?? defaults.iconSpacing ?? 4 + property string activeColorKey: cfg.activeColor ?? defaults.activeColor ?? "primary" + property string inactiveColorKey: cfg.inactiveColor ?? defaults.inactiveColor ?? "none" + + readonly property color activeColor: Color.resolveColorKey(activeColorKey) + readonly property color inactiveColor: inactiveColorKey === "none" ? Qt.alpha(Color.mOnSurfaceVariant, 0.3) : Color.resolveColorKey(inactiveColorKey) + readonly property color micColor: micActive ? activeColor : inactiveColor + readonly property color camColor: camActive ? activeColor : inactiveColor + readonly property color scrColor: scrActive ? activeColor : inactiveColor + + readonly property bool isVisible: !hideInactive || micActive || camActive || scrActive + + property real margins: removeMargins ? 0 : Style.marginM * 2 + + readonly property real contentWidth: isVertical ? Style.capsuleHeight : Math.round(layout.implicitWidth + margins) + readonly property real contentHeight: isVertical ? Math.round(layout.implicitHeight + margins) : Style.capsuleHeight + + implicitWidth: contentWidth + implicitHeight: contentHeight + + Layout.alignment: Qt.AlignVCenter + visible: root.isVisible + opacity: root.isVisible ? 1.0 : 0.0 + + function buildTooltip() { + var parts = []; + + if (micActive && micApps.length > 0) { + parts.push("Mic: " + micApps.join(", ")); + } + + if (camActive && camApps.length > 0) { + parts.push("Cam: " + camApps.join(", ")); + } + + if (scrActive && scrApps.length > 0) { + parts.push("Screen sharing: " + scrApps.join(", ")); + } + + return parts.length > 0 ? parts.join("\n") : ""; + } + + Rectangle { + id: visualCapsule + x: Style.pixelAlignCenter(parent.width, width) + y: Style.pixelAlignCenter(parent.height, height) + width: root.contentWidth + height: root.contentHeight + radius: Style.radiusM + color: Style.capsuleColor + border.color: Style.capsuleBorderColor + border.width: Style.capsuleBorderWidth + + Item { + id: layout + + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + + implicitWidth: iconsLayout.implicitWidth + implicitHeight: iconsLayout.implicitHeight + + GridLayout { + id: iconsLayout + + columns: root.isVertical ? 1 : 3 + rows: root.isVertical ? 3 : 1 + + rowSpacing: root.iconSpacing + columnSpacing: root.iconSpacing + + NIcon { + visible: micActive || !root.hideInactive + icon: micActive ? "microphone" : "microphone-off" + color: root.micColor + } + NIcon { + visible: camActive || !root.hideInactive + icon: camActive ? "camera" : "camera-off" + color: root.camColor + } + NIcon { + visible: scrActive || !root.hideInactive + icon: scrActive ? "screen-share" : "screen-share-off" + color: root.scrColor + } + } + } + } + + NPopupContextMenu { + id: contextMenu + + model: [ + { + "label": pluginApi?.tr("menu.settings"), + "action": "settings", + "icon": "settings" + }, + ] + + onTriggered: function (action) { + contextMenu.close(); + PanelService.closeContextMenu(screen); + if (action === "settings") { + BarService.openPluginSettings(root.screen, pluginApi.manifest); + } + } + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton | Qt.LeftButton + hoverEnabled: true + + onClicked: function (mouse) { + if (mouse.button === Qt.RightButton) { + PanelService.showContextMenu(contextMenu, root, screen); + } else if (mouse.button === Qt.LeftButton) { + if (pluginApi) pluginApi.openPanel(root.screen, root); + } + } + + onEntered: { + var tooltipText = buildTooltip(); + if (tooltipText) { + TooltipService.show(root, tooltipText, BarService.getTooltipDirection()); + } + } + onExited: TooltipService.hide() + } +} diff --git a/noctalia/.config/plugins/privacy-indicator/Main.qml b/noctalia/.config/plugins/privacy-indicator/Main.qml new file mode 100644 index 0000000..29d94a0 --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/Main.qml @@ -0,0 +1,316 @@ +import QtQuick +import Quickshell +import Quickshell.Io +import Quickshell.Services.Pipewire +import qs.Commons +import qs.Services.UI + +Item { + id: root + property var pluginApi: null + + // --- Logic extracted from BarWidget.qml --- + + property bool micActive: false + property bool camActive: false + property bool scrActive: false + property var micApps: [] + property var camApps: [] + property var scrApps: [] + + property var accessHistory: [] + + // Previous states for history tracking + property var _prevMicApps: [] + property var _prevCamApps: [] + property var _prevScrApps: [] + + // Get active color from settings or default + property var cfg: pluginApi?.pluginSettings || ({}) + 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 ?? "" + property string camFilterRegex: cfg.camFilterRegex ?? defaults.camFilterRegex ?? "" + + PwObjectTracker { + objects: Pipewire.ready ? Pipewire.nodes.values : [] + } + + Process { + id: cameraDetectionProcess + running: false + command: ["sh", "-c", "for dev in /sys/class/video4linux/video*; do [ -e \"$dev/name\" ] && grep -qv 'Metadata' \"$dev/name\" && dev_name=$(basename \"$dev\") && find /proc/[0-9]*/fd -lname \"/dev/$dev_name\" 2>/dev/null; done | cut -d/ -f3 | xargs -r ps -o comm= -p | sort -u | tr '\\n' ',' | sed 's/,$//'"] + stdout: StdioCollector { + onStreamFinished: { + var appsString = this.text.trim(); + var apps = appsString.length > 0 ? appsString.split(',') : []; + + var filterRegex = null; + if (root.camFilterRegex && root.camFilterRegex.length > 0) { + try { + filterRegex = new RegExp(root.camFilterRegex); + } catch (e) { + Logger.w("PrivacyIndicator: Invalid camFilterRegex:", root.camFilterRegex); + } + } + + var appNames = []; + for (var i = 0; i < apps.length; i++) { + var appName = apps[i]; + if (filterRegex && appName && filterRegex.test(appName)) continue; + if (appName && appNames.indexOf(appName) === -1) appNames.push(appName); + } + + root.camApps = appNames; + root.camActive = appNames.length > 0; + } + } + } + + + Timer { + interval: 1000 + repeat: true + running: true + triggeredOnStart: true + onTriggered: updatePrivacyState() + } + + function hasNodeLinks(node, links) { + for (var i = 0; i < links.length; i++) { + var link = links[i]; + if (link && (link.source === node || link.target === node)) return true; + } + return false; + } + + function getAppName(node) { + return node.properties["application.name"] || node.nickname || node.name || ""; + } + + 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; + if (!hasNodeLinks(node, links) || !node.properties) continue; + var mediaClass = node.properties["media.class"] || ""; + if (mediaClass === "Stream/Input/Audio") { + if (node.properties["stream.capture.sink"] === "true") continue; + + var appName = getAppName(node); + if (filterRegex && appName && filterRegex.test(appName)) continue; + + isActive = true; + if (appName && appNames.indexOf(appName) === -1) appNames.push(appName); + } + } + root.micActive = isActive; + root.micApps = appNames; + } + + function updateCameraState() { + cameraDetectionProcess.running = true; + } + + function isScreenShareNode(node) { + if (!node.properties) return false; + var mediaClass = node.properties["media.class"] || ""; + if (mediaClass.indexOf("Audio") >= 0) return false; + if (mediaClass.indexOf("Video") === -1) return false; + var mediaName = (node.properties["media.name"] || "").toLowerCase(); + if (mediaName.match(/^(xdph-streaming|gsr-default|game capture|screen|desktop|display|cast|webrtc|v4l2)/) || + mediaName === "gsr-default_output" || + mediaName.match(/screen-cast|screen-capture|desktop-capture|monitor-capture|window-capture|game-capture/i)) { + return true; + } + return false; + } + + function updateScreenShareState(nodes, links) { + var appNames = []; + var isActive = false; + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + if (!node || !hasNodeLinks(node, links) || !node.properties) continue; + if (isScreenShareNode(node)) { + isActive = true; + var appName = getAppName(node); + if (appName && appNames.indexOf(appName) === -1) appNames.push(appName); + } + } + root.scrActive = isActive; + root.scrApps = appNames; + } + + function updatePrivacyState() { + if (!Pipewire.ready) return; + var nodes = Pipewire.nodes.values || []; + var links = Pipewire.links.values || []; + updateMicrophoneState(nodes, links); + updateCameraState(); + updateScreenShareState(nodes, links); + } + + // --- History Persistence --- + + property string stateFile: "" + property bool isLoaded: false + + Component.onCompleted: { + // Setup state file path + Qt.callLater(() => { + if (typeof Settings !== 'undefined' && Settings.cacheDir) { + stateFile = Settings.cacheDir + "privacy-history.json"; + historyFileView.path = stateFile; + } + }); + } + + FileView { + id: historyFileView + printErrors: false + watchChanges: false + + adapter: JsonAdapter { + id: adapter + property var history: [] + } + + onLoaded: { + root.isLoaded = true; + if (adapter.history) { + // Restore history + root.accessHistory = adapter.history; + } + } + + onLoadFailed: error => { + // If file doesn't exist (error 2), we are ready to save new data + if (error === 2) { + root.isLoaded = true; + } else { + console.error("PrivacyIndicator: Failed to load history file:", error); + root.isLoaded = true; // Try to continue anyway + } + } + } + + function saveHistory() { + if (!stateFile || !isLoaded) return; + + adapter.history = root.accessHistory; + + // Ensure cache directory exists and save + try { + Quickshell.execDetached(["mkdir", "-p", Settings.cacheDir]); + Qt.callLater(() => { + try { + historyFileView.writeAdapter(); + } catch (e) { + console.error("PrivacyIndicator: Failed to save history", e); + } + }); + } catch (e) { + console.error("PrivacyIndicator: Failed to save history", e); + } + } + + function addToHistory(app, type, icon, colorKey, action) { + var time = new Date().toLocaleTimeString(Qt.locale(), Locale.ShortFormat); + var entry = { + "appName": app, + "type": type, + "icon": icon, + "colorKey": colorKey, + "time": time, + "timestamp": Date.now(), + "action": action // "started" or "stopped" + }; + var newHistory = [entry].concat(accessHistory); + if (newHistory.length > 50) newHistory = newHistory.slice(0, 50); // Increased limit as we have more entries now + accessHistory = newHistory; + saveHistory(); + } + + function clearHistory() { + accessHistory = []; + saveHistory(); + } + + 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++) { + var app = newApps[i]; + if (!oldApps || oldApps.indexOf(app) === -1) { + addToHistory(app, type, icon, colorKey, "started"); + } + } + } + + // Check for removed apps (Stopped) + if (oldApps) { + for (var j = 0; j < oldApps.length; j++) { + var oldApp = oldApps[j]; + if (!newApps || newApps.indexOf(oldApp) === -1) { + addToHistory(oldApp, type, icon, colorKey, "stopped"); + } + } + } + } + + + onMicAppsChanged: { + checkAppChanges(micApps, _prevMicApps, "Microphone", "microphone", activeColorKey); + _prevMicApps = micApps; + } + // Helper to detect activation edge + property bool oldMicActive: false + onMicActiveChanged: { + if (enableToast && micActive && !oldMicActive) { + ToastService.showNotice(pluginApi?.tr("toast.mic-on"), "", "microphone"); + } + oldMicActive = micActive + } + + property bool oldCamActive: false + onCamActiveChanged: { + if (enableToast && camActive && !oldCamActive) { + ToastService.showNotice(pluginApi?.tr("toast.cam-on"), "", "camera"); + } + oldCamActive = camActive + } + onCamAppsChanged: { + checkAppChanges(camApps, _prevCamApps, "Camera", "camera", activeColorKey); + _prevCamApps = camApps; + } + + property bool oldScrActive: false + onScrActiveChanged: { + if (enableToast && scrActive && !oldScrActive) { + ToastService.showNotice(pluginApi?.tr("toast.screen-on"), "", "screen-share"); + } + oldScrActive = scrActive + } + onScrAppsChanged: { + checkAppChanges(scrApps, _prevScrApps, "Screen", "screen-share", activeColorKey); + _prevScrApps = scrApps; + } + + +} diff --git a/noctalia/.config/plugins/privacy-indicator/Panel.qml b/noctalia/.config/plugins/privacy-indicator/Panel.qml new file mode 100644 index 0000000..0cfcc22 --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/Panel.qml @@ -0,0 +1,170 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import qs.Commons +import qs.Widgets + +Item { + id: root + + property var pluginApi: null + + // Standard panel properties + readonly property var geometryPlaceholder: panelContainer + property real contentPreferredWidth: 320 * Style.uiScaleRatio + property real contentPreferredHeight: 450 * Style.uiScaleRatio + + readonly property var mainInstance: pluginApi?.mainInstance + readonly property bool allowAttach: true + + Rectangle { + id: panelContainer + anchors.fill: parent + color: "transparent" + + ColumnLayout { + anchors.fill: parent + anchors.margins: Style.marginM + spacing: Style.marginM + + // Header Box + NBox { + Layout.fillWidth: true + Layout.preferredHeight: headerRow.implicitHeight + Style.marginM * 2 + + RowLayout { + id: headerRow + anchors.fill: parent + anchors.margins: Style.marginM + spacing: Style.marginS + + NIcon { + icon: "shield-check" + color: Color.mPrimary + pointSize: Style.fontSizeL + } + + NText { + Layout.fillWidth: true + text: pluginApi?.tr("history.title") + font.weight: Style.fontWeightBold + pointSize: Style.fontSizeL + color: Color.mOnSurface + } + + NIconButton { + icon: "trash" + baseSize: Style.baseWidgetSize * 0.8 + onClicked: { + if (mainInstance) mainInstance.clearHistory(); + } + } + } + } + + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + NScrollView { + id: scrollView + anchors.fill: parent + horizontalPolicy: ScrollBar.AlwaysOff + verticalPolicy: ScrollBar.AsNeeded + + ColumnLayout { + width: scrollView.availableWidth + spacing: Style.marginS + + Repeater { + model: mainInstance ? mainInstance.accessHistory : [] + + delegate: Rectangle { + Layout.fillWidth: true + implicitHeight: 56 * Style.uiScaleRatio + radius: Style.radiusM + color: Color.mSurfaceVariant + + RowLayout { + anchors.fill: parent + anchors.margins: Style.marginM + spacing: Style.marginM + + Rectangle { + width: 32 * Style.uiScaleRatio + height: 32 * Style.uiScaleRatio + radius: width/2 + color: Qt.alpha(iconColor, 0.1) + + readonly property color iconColor: Color.resolveColorKey(modelData.colorKey || "primary") + + NIcon { + anchors.centerIn: parent + icon: modelData.icon + color: parent.iconColor + pointSize: Style.fontSizeM + } + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 0 + + NText { + Layout.fillWidth: true + text: modelData.appName + elide: Text.ElideRight + font.weight: Style.fontWeightBold + pointSize: Style.fontSizeM + } + + RowLayout { + Layout.fillWidth: true + spacing: Style.marginS + + NText { + text: modelData.time + color: Qt.alpha(Color.mOnSurface, 0.7) + pointSize: Style.fontSizeS + } + + NText { + text: "•" + color: Qt.alpha(Color.mOnSurface, 0.3) + pointSize: Style.fontSizeS + } + + NText { + text: { + const action = modelData.action || "started"; + return pluginApi?.tr("history.action." + action) || action; + } + color: (modelData.action || "started") === "stopped" ? Color.resolveColorKey("error") : Color.resolveColorKey("primary") + font.weight: Style.fontWeightBold + pointSize: Style.fontSizeS + } + } + } + } + } + } + + // Empty state + NText { + Layout.alignment: Qt.AlignHCenter + visible: (!mainInstance || mainInstance.accessHistory.length === 0) + text: pluginApi?.tr("history.empty") + color: Qt.alpha(Color.mOnSurface, 0.5) + pointSize: Style.fontSizeM + Layout.topMargin: Style.marginL + } + + Item { Layout.fillHeight: true } // spacer + } + } + } + } + } +} diff --git a/noctalia/.config/plugins/privacy-indicator/README.md b/noctalia/.config/plugins/privacy-indicator/README.md new file mode 100644 index 0000000..d192040 --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/README.md @@ -0,0 +1,47 @@ +# Privacy Indicator Plugin + +A privacy indicator widget that monitors and displays when microphone, camera, or screen sharing is active on your system. + +## Features + +- **Microphone Monitoring**: Detects active microphone usage via Pipewire +- **Camera Monitoring**: Detects active camera usage by checking `/dev/video*` devices +- **Screen Sharing Detection**: Monitors screen sharing sessions via Pipewire +- **Visual Indicators**: Shows icons that change color based on active state + - Active: Primary color + - Inactive: Semi-transparent variant color +- **App Information**: Tooltip displays which applications are using each resource +- **Adaptive Layout**: Automatically adjusts layout for horizontal or vertical bar positions + +## Configuration + +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. +- **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 + +The widget displays three icons in the bar: +- **Microphone**: Shows when any app is using the microphone +- **Camera**: Shows when any app is accessing the camera +- **Screen Share**: Shows when screen sharing is active + +Hover over the widget to see a tooltip listing which applications are using each resource. + +## Requirements + +- Noctalia Shell 3.6.0 or higher +- Pipewire (for microphone and screen sharing detection) +- Access to `/dev/video*` devices (for camera detection) + +## Technical Details + +- Updates privacy state every second +- Uses Pipewire API to monitor audio/video streams +- Checks `/proc/[0-9]*/fd/` for camera device access +- Detects screen sharing by analyzing Pipewire node properties and media class diff --git a/noctalia/.config/plugins/privacy-indicator/Settings.qml b/noctalia/.config/plugins/privacy-indicator/Settings.qml new file mode 100644 index 0000000..a6acde9 --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/Settings.qml @@ -0,0 +1,142 @@ +import QtQuick +import QtQuick.Layouts +import qs.Commons +import qs.Widgets + +ColumnLayout { + id: root + + property var pluginApi: null + + property var cfg: pluginApi?.pluginSettings || ({}) + property var defaults: pluginApi?.manifest?.metadata?.defaultSettings || ({}) + + property bool hideInactive: cfg.hideInactive ?? defaults.hideInactive ?? false + property bool enableToast: cfg.enableToast ?? defaults.enableToast ?? true + property bool removeMargins: cfg.removeMargins ?? defaults.removeMargins ?? false + 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 + property string camFilterRegex: cfg.camFilterRegex ?? defaults.camFilterRegex + + spacing: Style.marginL + + Component.onCompleted: { + Logger.i("PrivacyIndicator", "Settings UI loaded"); + } + + ColumnLayout { + spacing: Style.marginM + Layout.fillWidth: true + + NToggle { + label: pluginApi?.tr("settings.hideInactive.label") + description: pluginApi?.tr("settings.hideInactive.desc") + + checked: root.hideInactive + onToggled: checked => { + root.hideInactive = checked; + } + } + + NToggle { + label: pluginApi?.tr("settings.enableToast.label") + description: pluginApi?.tr("settings.enableToast.desc") + + checked: root.enableToast + onToggled: checked => { + root.enableToast = checked; + } + } + + NToggle { + label: pluginApi?.tr("settings.removeMargins.label") + description: pluginApi?.tr("settings.removeMargins.desc") + + checked: root.removeMargins + onToggled: checked => { + root.removeMargins = checked; + } + } + + NColorChoice { + label: pluginApi?.tr("settings.activeColor.label") + description: pluginApi?.tr("settings.activeColor.desc") + currentKey: root.activeColor + onSelected: key => root.activeColor = key + } + + NColorChoice { + label: pluginApi?.tr("settings.inactiveColor.label") + description: pluginApi?.tr("settings.inactiveColor.desc") + currentKey: root.inactiveColor + onSelected: key => root.inactiveColor = key + noneColor: Qt.alpha(Color.mOnSurfaceVariant, 0.3) + noneOnColor: Qt.alpha(Color.mOnSurface, 0.7) + } + + NComboBox { + label: pluginApi?.tr("settings.iconSpacing.label") + description: pluginApi?.tr("settings.iconSpacing.desc") + + model: { + const labels = ["XXS", "XS", "S", "M", "L", "XL"]; + const values = [Style.marginXXS, Style.marginXS, Style.marginS, Style.marginM, Style.marginL, Style.marginXL]; + + const result = []; + for (var i = 0; i < labels.length; ++i) { + const v = values[i]; + result.push({ + key: v.toFixed(0), + name: `${labels[i]} (${v}px)` + }); + } + return result; + } + + // INFO: From my understanding, the toFixed(0) shouldn't be needed here and there, but without the + // current key does not show when opening the settings window. + currentKey: root.iconSpacing.toFixed(0) + onSelected: key => root.iconSpacing = key + } + + NTextInput { + Layout.fillWidth: true + label: pluginApi?.tr("settings.micFilterRegex.label") + description: pluginApi?.tr("settings.micFilterRegex.desc") + placeholderText: "effect_input.rnnoise|easyeffects" + text: root.micFilterRegex + onTextChanged: root.micFilterRegex = text + } + + NTextInput { + Layout.fillWidth: true + label: pluginApi?.tr("settings.camFilterRegex.label") + description: pluginApi?.tr("settings.camFilterRegex.desc") + placeholderText: "droidcam" + text: root.camFilterRegex + onTextChanged: root.camFilterRegex = text + } + } + + function saveSettings() { + if (!pluginApi) { + Logger.e("PrivacyIndicator", "Cannot save settings: pluginApi is null"); + return; + } + + pluginApi.pluginSettings.hideInactive = root.hideInactive; + pluginApi.pluginSettings.enableToast = root.enableToast; + pluginApi.pluginSettings.iconSpacing = root.iconSpacing; + pluginApi.pluginSettings.removeMargins = root.removeMargins; + pluginApi.pluginSettings.activeColor = root.activeColor; + pluginApi.pluginSettings.inactiveColor = root.inactiveColor; + pluginApi.pluginSettings.micFilterRegex = root.micFilterRegex; + pluginApi.pluginSettings.camFilterRegex = root.camFilterRegex; + + pluginApi.saveSettings(); + + Logger.i("PrivacyIndicator", "Settings saved successfully"); + } +} diff --git a/noctalia/.config/plugins/privacy-indicator/i18n/de.json b/noctalia/.config/plugins/privacy-indicator/i18n/de.json new file mode 100644 index 0000000..5817042 --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/i18n/de.json @@ -0,0 +1,58 @@ +{ + "menu": { + "settings": "Widget Einstellungen" + }, + "settings": { + "activeColor": { + "desc": "Farbe der Symbole, wenn sie aktiv sind.", + "label": "Aktive Farbsymbol" + }, + "hideInactive": { + "desc": "Mikrofon-, Kamera- und Bildschirmsymbole ausblenden, wenn sie inaktiv sind.", + "label": "Inaktive Zustände ausblenden" + }, + "enableToast": { + "desc": "Zeige Toast Mitteilungen an, wenn sich einer der Zustände ändert.", + "label": "Aktiviere Toast Mitteilungen" + }, + "inactiveColor": { + "desc": "Farbe der Symbole, wenn sie inaktiv sind.", + "label": "Inaktive Farbsymbol" + }, + "iconSpacing": { + "desc": "Den Abstand zwischen den Symbolen festlegen.", + "label": "Symbolabstand" + }, + "removeMargins": { + "desc": "Alle äußeren Ränder des Widgets entfernen.", + "label": "Ränder entfernen" + }, + "micFilterRegex": { + "desc": "Regex Muster zum Herausfiltern von Mikrofon-Apps. Entsprechende Apps werden vollständig von der Erkennung ausgeschlossen.", + "label": "Regex für Mikrofonfilter" + }, + "camFilterRegex": { + "desc": "Regex-Muster zum Herausfiltern von Kamera-Apps. Entsprechende Apps werden vollständig von der Erkennung ausgeschlossen.", + "label": "Regex für Kamerafilter" + } + }, + "tooltip": { + "cam-on": "Kamera: {apps}", + "mic-on": "Mikrofon: {apps}", + "screen-on": "Bildschirmfreigabe: {apps}" + }, + "toast": { + "cam-on": "Kamera ist aktiv", + "mic-on": "Mikrofon ist aktiv", + "screen-on": "Bildschirmfreigabe ist aktiv" + }, + "history": { + "title": "Zugriffsverlauf", + "empty": "Kein kürzlicher Zugriff", + "clear": "Leeren", + "action": { + "started": "Gestartet", + "stopped": "Beendet" + } + } +} diff --git a/noctalia/.config/plugins/privacy-indicator/i18n/en.json b/noctalia/.config/plugins/privacy-indicator/i18n/en.json new file mode 100644 index 0000000..fa65418 --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/i18n/en.json @@ -0,0 +1,58 @@ +{ + "menu": { + "settings": "Widget settings" + }, + "settings": { + "activeColor": { + "desc": "Color of the icons when active.", + "label": "Active icon color" + }, + "hideInactive": { + "desc": "Hide microphone, camera, and screen icons when there are inactive.", + "label": "Hide inactive states" + }, + "enableToast": { + "desc": "Show toast messages when one of the states changes.", + "label": "Enable toast notifications" + }, + "inactiveColor": { + "desc": "Color of the icons when inactive.", + "label": "Inactive icon color" + }, + "iconSpacing": { + "desc": "Set the spacing between the icons.", + "label": "Icon spacing" + }, + "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" + }, + "camFilterRegex": { + "desc": "Regex pattern to filter out camera applications. Matching apps are completely excluded from detection.", + "label": "Camera filter regex" + } + }, + "tooltip": { + "cam-on": "Camera: {apps}", + "mic-on": "Microphone: {apps}", + "screen-on": "Screen sharing: {apps}" + }, + "toast": { + "cam-on": "Camera is active", + "mic-on": "Microphone is active", + "screen-on": "Screen sharing is active" + }, + "history": { + "title": "Access History", + "empty": "No recent access", + "clear": "Clear", + "action": { + "started": "Started", + "stopped": "Stopped" + } + } +} diff --git a/noctalia/.config/plugins/privacy-indicator/i18n/es.json b/noctalia/.config/plugins/privacy-indicator/i18n/es.json new file mode 100644 index 0000000..93a48a7 --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/i18n/es.json @@ -0,0 +1,46 @@ +{ + "settings": { + "hideInactive": { + "desc": "Ocultar los iconos de micrófono, cámara y pantalla cuando estén inactivos.", + "label": "Ocultar estados inactivos" + }, + "iconSpacing": { + "desc": "Configurar el espacio entre los iconos.", + "label": "Espaciado de iconos" + }, + "removeMargins": { + "desc": "Quita todos los márgenes externos del widget.", + "label": "Quitar márgenes" + }, + "activeColor": { + "desc": "Color de los iconos cuando están activos.", + "label": "Color de icono activo" + }, + "inactiveColor": { + "desc": "Color de los iconos cuando están inactivos.", + "label": "Color de icono inactivo" + } + }, + "tooltip": { + "cam-on": "Cámara: {apps}", + "mic-on": "Micrófono: {apps}", + "screen-on": "Compartir pantalla: {apps}" + }, + "toast": { + "cam-on": "La cámara está activa", + "mic-on": "El micrófono está activo", + "screen-on": "Compartir pantalla está activo" + }, + "menu": { + "settings": "Configuración del widget" + }, + "history": { + "title": "Historial de acceso", + "empty": "Sin accesos recientes", + "clear": "Borrar", + "action": { + "started": "Iniciado", + "stopped": "Detenido" + } + } +}
\ No newline at end of file diff --git a/noctalia/.config/plugins/privacy-indicator/i18n/fr.json b/noctalia/.config/plugins/privacy-indicator/i18n/fr.json new file mode 100644 index 0000000..814c07d --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/i18n/fr.json @@ -0,0 +1,58 @@ +{ + "menu": { + "settings": "Paramètres du widget" + }, + "settings": { + "activeColor": { + "desc": "Couleur des icônes lorsqu'elles sont actives.", + "label": "Couleur d'icône active" + }, + "hideInactive": { + "desc": "Masquer les icônes du micro, de la caméra et de l’écran lorsqu’ils sont inactifs.", + "label": "Masquer les états inactifs" + }, + "enableToast": { + "desc": "Afficher des messages toast lorsque l'un des états change.", + "label": "Activer les notifications toast" + }, + "inactiveColor": { + "desc": "Couleur des icônes lorsqu'elles sont inactives.", + "label": "Couleur d'icône inactive" + }, + "iconSpacing": { + "desc": "Définir l’espacement entre les icônes.", + "label": "Espacement des icônes" + }, + "removeMargins": { + "desc": "Supprime toutes les marges extérieures du widget.", + "label": "Supprimer les marges" + }, + "micFilterRegex": { + "desc": "Motif Regex pour filtrer les applications de microphone. Les applications correspondantes sont complètement exclues de la détection.", + "label": "Regex de filtrage du microphone" + }, + "camFilterRegex": { + "desc": "Motif Regex pour filtrer les applications de caméra. Les applications correspondantes sont complètement exclues de la détection.", + "label": "Regex de filtrage de la caméra" + } + }, + "tooltip": { + "cam-on": "Caméra: {apps}", + "mic-on": "Microphone: {apps}", + "screen-on": "Partage d'écran: {apps}" + }, + "toast": { + "cam-on": "La caméra est active", + "mic-on": "Le microphone est actif", + "screen-on": "Le partage d'écran est actif" + }, + "history": { + "title": "Historique d'accès", + "empty": "Aucun accès récent", + "clear": "Effacer", + "action": { + "started": "Démarré", + "stopped": "Arrêté" + } + } +} diff --git a/noctalia/.config/plugins/privacy-indicator/i18n/hu.json b/noctalia/.config/plugins/privacy-indicator/i18n/hu.json new file mode 100644 index 0000000..4a7ba8c --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/i18n/hu.json @@ -0,0 +1,46 @@ +{ + "settings": { + "hideInactive": { + "desc": "Mikrofon, kamera és képernyő ikonok elrejtése, ha éppen nincsenek használatban.", + "label": "Inaktív állapotok elrejtése" + }, + "iconSpacing": { + "desc": "Állítsa be az ikonok közötti távolságot.", + "label": "Ikon távolság" + }, + "removeMargins": { + "desc": "Távolítsd el a widget összes külső margóját.", + "label": "Margók eltávolítása" + }, + "activeColor": { + "desc": "Az ikonok színe, amikor aktívak.", + "label": "Aktív ikon színe" + }, + "inactiveColor": { + "desc": "Az ikonok színe, amikor inaktívak.", + "label": "Inaktív ikon színe" + } + }, + "tooltip": { + "cam-on": "Kamera: {apps}", + "mic-on": "Mikrofon: {apps}", + "screen-on": "Képernyőmegosztás: {apps}" + }, + "toast": { + "cam-on": "A kamera aktív", + "mic-on": "A mikrofon aktív", + "screen-on": "Képernyőmegosztás aktív" + }, + "menu": { + "settings": "Widget beállítások" + }, + "history": { + "title": "Hozzáférési előzmények", + "empty": "Nincs nemrégiben történt hozzáférés", + "clear": "Törlés", + "action": { + "started": "Elindítva", + "stopped": "Leállítva" + } + } +}
\ No newline at end of file diff --git a/noctalia/.config/plugins/privacy-indicator/i18n/it.json b/noctalia/.config/plugins/privacy-indicator/i18n/it.json new file mode 100644 index 0000000..d0eee79 --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/i18n/it.json @@ -0,0 +1,46 @@ +{ + "settings": { + "hideInactive": { + "desc": "Nascondi le icone di microfono, fotocamera e schermo quando sono inattive.", + "label": "Nascondi stati inattivi" + }, + "iconSpacing": { + "desc": "Imposta la distanza tra le icone.", + "label": "Spaziatura delle icone" + }, + "removeMargins": { + "desc": "Rimuove tutti i margini esterni del widget.", + "label": "Rimuovi margini" + }, + "activeColor": { + "desc": "Colore delle icone quando sono attive.", + "label": "Colore icona attiva" + }, + "inactiveColor": { + "desc": "Colore delle icone quando sono inattive.", + "label": "Colore icona inattiva" + } + }, + "tooltip": { + "cam-on": "Kamera: {apps}", + "mic-on": "Mikrofonoa: {apps}", + "screen-on": "Ekran-partaĝado: {apps}" + }, + "toast": { + "cam-on": "La fotocamera è attiva", + "mic-on": "Il microfono è attivo", + "screen-on": "La condivisione dello schermo è attiva" + }, + "menu": { + "settings": "Impostazioni widget" + }, + "history": { + "title": "Cronologia accessi", + "empty": "Nessun accesso recente", + "clear": "Pulisci", + "action": { + "started": "Iniziato", + "stopped": "Terminato" + } + } +}
\ No newline at end of file diff --git a/noctalia/.config/plugins/privacy-indicator/i18n/ja.json b/noctalia/.config/plugins/privacy-indicator/i18n/ja.json new file mode 100644 index 0000000..ba61aeb --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/i18n/ja.json @@ -0,0 +1,46 @@ +{ + "settings": { + "hideInactive": { + "desc": "マイク・カメラ・画面共有のアイコンを、非アクティブなときは非表示にします。", + "label": "非アクティブ状態を非表示" + }, + "iconSpacing": { + "desc": "アイコン同士の間隔を設定します。", + "label": "アイコン間隔" + }, + "removeMargins": { + "desc": "ウィジェットの外側の余白をすべて削除します。", + "label": "余白を削除" + }, + "activeColor": { + "desc": "アクティブ時のアイコンの色。", + "label": "アクティブ時の色" + }, + "inactiveColor": { + "desc": "非アクティブ時のアイコンの色。", + "label": "非アクティブ時の色" + } + }, + "tooltip": { + "cam-on": "カメラ: {apps}", + "mic-on": "マイク: {apps}", + "screen-on": "画面共有: {apps}" + }, + "toast": { + "cam-on": "カメラがアクティブです", + "mic-on": "マイクがアクティブです", + "screen-on": "画面共有がアクティブです" + }, + "menu": { + "settings": "ウィジェット設定" + }, + "history": { + "title": "アクセス履歴", + "empty": "最近のアクセスはありません", + "clear": "クリア", + "action": { + "started": "開始", + "stopped": "停止" + } + } +}
\ No newline at end of file diff --git a/noctalia/.config/plugins/privacy-indicator/i18n/ko.json b/noctalia/.config/plugins/privacy-indicator/i18n/ko.json new file mode 100644 index 0000000..e104202 --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/i18n/ko.json @@ -0,0 +1,58 @@ +{ + "menu": { + "settings": "위젯 설정" + }, + "settings": { + "activeColor": { + "desc": "활성 상태일 때 아이콘에 적용되는 색상입니다.", + "label": "활성 아이콘 색상" + }, + "hideInactive": { + "desc": "비활성화된 상태의 마이크, 카메라, 화면 아이콘을 숨깁니다.", + "label": "비활성 상태 숨기기" + }, + "enableToast": { + "desc": "상태가 변경될 때 토스트 알림을 표시합니다.", + "label": "토스트 알림 활성화" + }, + "inactiveColor": { + "desc": "비활성 상태일 때 아이콘에 적용되는 색상입니다.", + "label": "비활성 아이콘 색상" + }, + "iconSpacing": { + "desc": "아이콘 사이의 간격을 설정합니다.", + "label": "아이콘 간격" + }, + "removeMargins": { + "desc": "위젯의 모든 외곽 여백을 제거합니다.", + "label": "여백 제거" + }, + "micFilterRegex": { + "desc": "마이크를 사용하는 애플리케이션을 필터링하기 위한 정규식을 지정합니다. 일치하는 애플리케이션은 상태 감지에서 완전히 제외됩니다.", + "label": "마이크 필터 정규식" + }, + "camFilterRegex": { + "desc": "카메라를 사용하는 애플리케이션을 필터링하기 위한 정규식을 지정합니다. 일치하는 애플리케이션은 상태 감지에서 완전히 제외됩니다.", + "label": "카메라 필터 정규식" + } + }, + "tooltip": { + "cam-on": "카메라: {apps}", + "mic-on": "마이크: {apps}", + "screen-on": "화면 공유: {apps}" + }, + "toast": { + "cam-on": "카메라가 활성화되었습니다.", + "mic-on": "마이크가 활성화되었습니다.", + "screen-on": "화면 공유가 활성화되었습니다." + }, + "history": { + "title": "접근 기록", + "empty": "최근에 접근한 기록 없음", + "clear": "비우기", + "action": { + "started": "시작됨", + "stopped": "정지됨" + } + } +} diff --git a/noctalia/.config/plugins/privacy-indicator/i18n/ku.json b/noctalia/.config/plugins/privacy-indicator/i18n/ku.json new file mode 100644 index 0000000..6a58f84 --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/i18n/ku.json @@ -0,0 +1,46 @@ +{ + "settings": { + "hideInactive": { + "desc": "Îkonên mîkrofon, kamera û ekranê dema ku neçalak bin veşêre.", + "label": "Rewşa neçalak veşêre" + }, + "iconSpacing": { + "desc": "Cihê di navbera îkonan de diyar bike.", + "label": "Dûrahiya îkonan" + }, + "removeMargins": { + "desc": "Hemû marjînalên derveyî yên widgetê rake.", + "label": "Derdestên derxînin" + }, + "activeColor": { + "desc": "Color of the icons when active.", + "label": "Active icon color" + }, + "inactiveColor": { + "desc": "Color of the icons when inactive.", + "label": "Inactive icon color" + } + }, + "tooltip": { + "cam-on": "Kamera: {apps}", + "mic-on": "Mîkrofon: {apps}", + "screen-on": "Parvekirina ekranê: {apps}" + }, + "toast": { + "cam-on": "Kamera çalak e", + "mic-on": "Mîkrofon çalak e", + "screen-on": "Parvekirina ekranê çalak e" + }, + "menu": { + "settings": "Widget settings" + }, + "history": { + "title": "Access History", + "empty": "No recent access", + "clear": "Clear", + "action": { + "started": "Started", + "stopped": "Stopped" + } + } +}
\ No newline at end of file diff --git a/noctalia/.config/plugins/privacy-indicator/i18n/nl.json b/noctalia/.config/plugins/privacy-indicator/i18n/nl.json new file mode 100644 index 0000000..d0b031d --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/i18n/nl.json @@ -0,0 +1,46 @@ +{ + "settings": { + "hideInactive": { + "desc": "Verberg de pictogrammen voor microfoon, camera en scherm wanneer ze inactief zijn.", + "label": "Inactieve status verbergen" + }, + "iconSpacing": { + "desc": "Stel de afstand tussen de pictogrammen in.", + "label": "Pictogramafstand" + }, + "removeMargins": { + "desc": "Verwijdert alle buitenste marges van de widget.", + "label": "Marges verwijderen" + }, + "activeColor": { + "desc": "Kleur van de pictogrammen wanneer ze actief zijn.", + "label": "Actieve pictogramkleur" + }, + "inactiveColor": { + "desc": "Kleur van de pictogrammen wanneer ze inactief zijn.", + "label": "Inactieve pictogramkleur" + } + }, + "tooltip": { + "cam-on": "Camera: {apps}", + "mic-on": "Microfoon: {apps}", + "screen-on": "Schermdeling: {apps}" + }, + "toast": { + "cam-on": "Camera is actief", + "mic-on": "Microfoon is actief", + "screen-on": "Scherm delen is actief" + }, + "menu": { + "settings": "Widget instellingen" + }, + "history": { + "title": "Toegangsgeschiedenis", + "empty": "Geen recente toegang", + "clear": "Wissen", + "action": { + "started": "Gestart", + "stopped": "Gestopt" + } + } +}
\ No newline at end of file diff --git a/noctalia/.config/plugins/privacy-indicator/i18n/pl.json b/noctalia/.config/plugins/privacy-indicator/i18n/pl.json new file mode 100644 index 0000000..9e05d3a --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/i18n/pl.json @@ -0,0 +1,46 @@ +{ + "settings": { + "hideInactive": { + "desc": "Ukryj ikony mikrofonu, kamery i ekranu, gdy są nieaktywne.", + "label": "Ukryj nieaktywne stany" + }, + "iconSpacing": { + "desc": "Ustaw odstęp między ikonami.", + "label": "Odstępy ikon" + }, + "removeMargins": { + "desc": "Usuń wszystkie zewnętrzne marginesy widżetu.", + "label": "Usuń marginesy" + }, + "activeColor": { + "desc": "Kolor ikon, gdy są aktywne.", + "label": "Kolor aktywnej ikony" + }, + "inactiveColor": { + "desc": "Kolor ikon, gdy są nieaktywne.", + "label": "Kolor nieaktywnej ikony" + } + }, + "tooltip": { + "cam-on": "Kamera: {apps}", + "mic-on": "Mikrofon: {apps}", + "screen-on": "Udostępnianie ekranu: {apps}" + }, + "toast": { + "cam-on": "Kamera jest aktywna", + "mic-on": "Mikrofon jest aktywny", + "screen-on": "Udostępnianie ekranu jest aktywne" + }, + "menu": { + "settings": "Ustawienia widżetu" + }, + "history": { + "title": "Historia dostępu", + "empty": "Brak ostatnich dostępów", + "clear": "Wyczyść", + "action": { + "started": "Rozpoczęto", + "stopped": "Zatrzymano" + } + } +}
\ No newline at end of file diff --git a/noctalia/.config/plugins/privacy-indicator/i18n/pt.json b/noctalia/.config/plugins/privacy-indicator/i18n/pt.json new file mode 100644 index 0000000..068a528 --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/i18n/pt.json @@ -0,0 +1,46 @@ +{ + "settings": { + "hideInactive": { + "desc": "Oculta os ícones de microfone, câmera e tela quando estiverem inativos.", + "label": "Ocultar estados inativos" + }, + "iconSpacing": { + "desc": "Define o espaçamento entre os ícones.", + "label": "Espaçamento dos ícones" + }, + "removeMargins": { + "desc": "Remove todas as margens externas do widget.", + "label": "Remover margens" + }, + "activeColor": { + "desc": "Cor dos ícones quando ativos.", + "label": "Cor do ícone ativo" + }, + "inactiveColor": { + "desc": "Cor dos ícones quando inativos.", + "label": "Cor do ícone inativo" + } + }, + "tooltip": { + "cam-on": "Câmera: {apps}", + "mic-on": "Microfone: {apps}", + "screen-on": "Compartilhamento de tela: {apps}" + }, + "toast": { + "cam-on": "A câmera está ativa", + "mic-on": "O microfone está ativo", + "screen-on": "O compartilhamento de tela está ativo" + }, + "menu": { + "settings": "Configurações do widget" + }, + "history": { + "title": "Histórico de acesso", + "empty": "Sem acessos recentes", + "clear": "Limpar", + "action": { + "started": "Iniciado", + "stopped": "Interrompido" + } + } +}
\ No newline at end of file diff --git a/noctalia/.config/plugins/privacy-indicator/i18n/ru.json b/noctalia/.config/plugins/privacy-indicator/i18n/ru.json new file mode 100644 index 0000000..152e7d8 --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/i18n/ru.json @@ -0,0 +1,46 @@ +{ + "settings": { + "hideInactive": { + "desc": "Скрывать значки микрофона, камеры и экрана, когда они неактивны.", + "label": "Скрывать неактивные состояния" + }, + "iconSpacing": { + "desc": "Задать расстояние между значками.", + "label": "Интервал между значками" + }, + "removeMargins": { + "desc": "Удаляет все внешние отступы виджета.", + "label": "Убрать отступы" + }, + "activeColor": { + "desc": "Цвет иконок при активации.", + "label": "Цвет активной иконки" + }, + "inactiveColor": { + "desc": "Цвет иконок в спокойном состоянии.", + "label": "Цвет неактивной иконки" + } + }, + "tooltip": { + "cam-on": "Камера: {apps}", + "mic-on": "Микрофон: {apps}", + "screen-on": "Демонстрация экрана: {apps}" + }, + "toast": { + "cam-on": "Камера активна", + "mic-on": "Микрофон активен", + "screen-on": "Демонстрация экрана активна" + }, + "menu": { + "settings": "Настройки виджета" + }, + "history": { + "title": "История доступа", + "empty": "Нет недавних доступов", + "clear": "Очистить", + "action": { + "started": "Начато", + "stopped": "Остановлено" + } + } +}
\ No newline at end of file diff --git a/noctalia/.config/plugins/privacy-indicator/i18n/tr.json b/noctalia/.config/plugins/privacy-indicator/i18n/tr.json new file mode 100644 index 0000000..016f1be --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/i18n/tr.json @@ -0,0 +1,46 @@ +{ + "settings": { + "hideInactive": { + "desc": "Mikrofon, kamera ve ekran simgelerini pasif olduklarında gizle.", + "label": "Pasif durumları gizle" + }, + "iconSpacing": { + "desc": "Simgeler arasındaki boşluğu ayarla.", + "label": "Simge aralığı" + }, + "removeMargins": { + "desc": "Widget’ın tüm dış kenar boşluklarını kaldırır.", + "label": "Kenarlıkları kaldır" + }, + "activeColor": { + "desc": "Aktif olduğunda simgelerin rengi.", + "label": "Aktif simge rengi" + }, + "inactiveColor": { + "desc": "Devre dışı olduğunda simgelerin rengi.", + "label": "Pasif simge rengi" + } + }, + "tooltip": { + "cam-on": "Kamera: {apps}", + "mic-on": "Mikrofon: {apps}", + "screen-on": "Ekran paylaşımı: {apps}" + }, + "toast": { + "cam-on": "Kamera aktif", + "mic-on": "Mikrofon aktif", + "screen-on": "Ekran paylaşımı aktif" + }, + "menu": { + "settings": "Bileşen ayarları" + }, + "history": { + "title": "Erişim Geçmişi", + "empty": "Yakın zamanda erişim yok", + "clear": "Temizle", + "action": { + "started": "Başlatıldı", + "stopped": "Durduruldu" + } + } +}
\ No newline at end of file diff --git a/noctalia/.config/plugins/privacy-indicator/i18n/uk-UA.json b/noctalia/.config/plugins/privacy-indicator/i18n/uk-UA.json new file mode 100644 index 0000000..5b872a7 --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/i18n/uk-UA.json @@ -0,0 +1,46 @@ +{ + "settings": { + "hideInactive": { + "desc": "Приховувати значки мікрофона, камери та екрана, коли вони неактивні.", + "label": "Приховувати неактивні стани" + }, + "iconSpacing": { + "desc": "Встановити відстань між значками.", + "label": "Інтервал між значками" + }, + "removeMargins": { + "desc": "Видаляє всі зовнішні відступи віджета.", + "label": "Прибрати відступи" + }, + "activeColor": { + "desc": "Колір іконок при активації.", + "label": "Колір активної іконки" + }, + "inactiveColor": { + "desc": "Колір іконок у спокійному стані.", + "label": "Колір неактивної іконки" + } + }, + "tooltip": { + "cam-on": "Камера: {apps}", + "mic-on": "Мікрофон: {apps}", + "screen-on": "Демонстрація екрана: {apps}" + }, + "toast": { + "cam-on": "Камера активна", + "mic-on": "Мікрофон активний", + "screen-on": "Демонстрація екрана активна" + }, + "menu": { + "settings": "Налаштування віджета" + }, + "history": { + "title": "Історія доступу", + "empty": "Немає недавніх доступів", + "clear": "Очистити", + "action": { + "started": "Розпочато", + "stopped": "Зупинено" + } + } +}
\ No newline at end of file diff --git a/noctalia/.config/plugins/privacy-indicator/i18n/vi.json b/noctalia/.config/plugins/privacy-indicator/i18n/vi.json new file mode 100644 index 0000000..6cd6d76 --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/i18n/vi.json @@ -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" + } + } +} diff --git a/noctalia/.config/plugins/privacy-indicator/i18n/zh-CN.json b/noctalia/.config/plugins/privacy-indicator/i18n/zh-CN.json new file mode 100644 index 0000000..5665d78 --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/i18n/zh-CN.json @@ -0,0 +1,46 @@ +{ + "settings": { + "hideInactive": { + "desc": "在麦克风、摄像头和屏幕图标处于非活动状态时将其隐藏。", + "label": "隐藏非活动状态" + }, + "iconSpacing": { + "desc": "设置图标之间的间距。", + "label": "图标间距" + }, + "removeMargins": { + "desc": "移除小部件所有外部边距。", + "label": "移除边距" + }, + "activeColor": { + "desc": "激活图标的颜色。", + "label": "激活图标颜色" + }, + "inactiveColor": { + "desc": "未激活图标的颜色。", + "label": "未激活图标颜色" + } + }, + "tooltip": { + "cam-on": "摄像头: {apps}", + "mic-on": "麦克风: {apps}", + "screen-on": "屏幕共享: {apps}" + }, + "toast": { + "cam-on": "摄像头已激活", + "mic-on": "麦克风已激活", + "screen-on": "屏幕共享已激活" + }, + "menu": { + "settings": "小部件设置" + }, + "history": { + "title": "访问历史", + "empty": "无最近访问", + "clear": "清除", + "action": { + "started": "已开始", + "stopped": "已停止" + } + } +}
\ No newline at end of file diff --git a/noctalia/.config/plugins/privacy-indicator/i18n/zh-TW.json b/noctalia/.config/plugins/privacy-indicator/i18n/zh-TW.json new file mode 100644 index 0000000..febeadc --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/i18n/zh-TW.json @@ -0,0 +1,46 @@ +{ + "settings": { + "hideInactive": { + "desc": "當麥克風, 攝影機及螢幕分享沒有啟動時就直接隱藏", + "label": "隱藏未啟動的狀態" + }, + "iconSpacing": { + "desc": "設定圖示之間的留空", + "label": "圖示間距" + }, + "removeMargins": { + "desc": "移除小工具外面的所有邊距", + "label": "移除邊距" + }, + "activeColor": { + "desc": "圖示啟用時的顏色。", + "label": "啟用圖示顏色" + }, + "inactiveColor": { + "desc": "圖示未啟用時的顏色。", + "label": "未啟用圖示顏色" + } + }, + "tooltip": { + "cam-on": "攝影機: {apps}", + "mic-on": "麥克風: {apps}", + "screen-on": "螢幕分享: {apps}" + }, + "toast": { + "cam-on": "攝影機已啟用", + "mic-on": "麥克風已啟用", + "screen-on": "螢幕分享已啟用" + }, + "menu": { + "settings": "小工具設定" + }, + "history": { + "title": "存取紀錄", + "empty": "無最近存取", + "clear": "清除", + "action": { + "started": "已開始", + "stopped": "已停止" + } + } +}
\ No newline at end of file diff --git a/noctalia/.config/plugins/privacy-indicator/manifest.json b/noctalia/.config/plugins/privacy-indicator/manifest.json new file mode 100644 index 0000000..4618fc4 --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/manifest.json @@ -0,0 +1,37 @@ +{ + "id": "privacy-indicator", + "name": "Privacy Indicator", + "version": "1.2.13", + "minNoctaliaVersion": "3.6.0", + "author": "Noctalia Team", + "official": true, + "license": "MIT", + "repository": "https://github.com/noctalia-dev/noctalia-plugins", + "description": "A privacy indicator widget that shows when microphone, camera or screen sharing is active.", + "tags": [ + "Bar", + "Privacy", + "Indicator" + ], + "entryPoints": { + "main": "Main.qml", + "barWidget": "BarWidget.qml", + "panel": "Panel.qml", + "settings": "Settings.qml" + }, + "dependencies": { + "plugins": [] + }, + "metadata": { + "defaultSettings": { + "hideInactive": false, + "enableToast": true, + "removeMargins": false, + "iconSpacing": 4, + "activeColor": "primary", + "inactiveColor": "none", + "micFilterRegex": "", + "camFilterRegex": "" + } + } +} diff --git a/noctalia/.config/plugins/privacy-indicator/preview.png b/noctalia/.config/plugins/privacy-indicator/preview.png Binary files differnew file mode 100644 index 0000000..0db7b7c --- /dev/null +++ b/noctalia/.config/plugins/privacy-indicator/preview.png |
