This commit is contained in:
Dmitri 2026-04-15 16:30:27 +02:00
parent 236be03fab
commit 55ac166402
Signed by: kanopo
GPG Key ID: 759ADD40E3132AC7
35 changed files with 1887 additions and 57 deletions

View File

@ -0,0 +1 @@
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin

View File

@ -1,3 +1,6 @@
[main]
include=~/.config/foot/themes/noctalia
# -*- conf -*-
# shell=$SHELL (if set, otherwise user's default shell from /etc/passwd)

22
foot/themes/noctalia Normal file
View File

@ -0,0 +1,22 @@
[colors-dark]
foreground=ebdbb2
background=282828
regular0=282828
regular1=cc241d
regular2=98971a
regular3=d79921
regular4=458588
regular5=b16286
regular6=689d6a
regular7=a89984
bright0=928374
bright1=fb4934
bright2=b8bb26
bright3=fabd2f
bright4=83a598
bright5=d3869b
bright6=8ec07c
bright7=ebdbb2
selection-foreground=ebdbb2
selection-background=665c54
cursor=282828 ebdbb2

View File

@ -16,3 +16,5 @@ ln -s ~/Documents/dotfiles/zathura ~/.config/
ln -s ~/Documents/dotfiles/opencode ~/.config/
ln -s ~/Documents/dotfiles/.zshrc ~/
ln -s ~/Documents/dotfiles/.gitconfig ~/
mkdir -p ~/.config/environment.d
ln -s ~/Documents/dotfiles/environment.d/10-path.conf ~/.config/environment.d/10-path.conf

View File

@ -7,6 +7,10 @@
}
],
"states": {
"privacy-indicator": {
"enabled": true,
"sourceUrl": "https://github.com/noctalia-dev/noctalia-plugins"
}
},
"version": 2
}

View File

@ -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()
}
}

View File

@ -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++) {
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;
}
}

View File

@ -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
}
}
}
}
}
}

View File

@ -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

View File

@ -0,0 +1,141 @@
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.saveSettings();
Logger.i("PrivacyIndicator", "Settings saved successfully");
}
}

View File

@ -0,0 +1,54 @@
{
"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"
}
},
"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"
}
}
}

View File

@ -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"
}
}
}

View File

@ -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"
}
}
}

View File

@ -0,0 +1,54 @@
{
"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 lorsquils 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 lespacement 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"
}
},
"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é"
}
}
}

View File

@ -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"
}
}
}

View File

@ -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"
}
}
}

View File

@ -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": "停止"
}
}
}

View File

@ -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"
}
}
}

View File

@ -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"
}
}
}

View File

@ -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"
}
}
}

View File

@ -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"
}
}
}

View File

@ -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": "Остановлено"
}
}
}

View File

@ -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"
}
}
}

View File

@ -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": "Зупинено"
}
}
}

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

@ -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": "已停止"
}
}
}

View File

@ -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": "已停止"
}
}
}

View File

@ -0,0 +1,37 @@
{
"id": "privacy-indicator",
"name": "Privacy Indicator",
"version": "1.2.9",
"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": ""
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,9 @@
{
"hideInactive": true,
"enableToast": true,
"removeMargins": false,
"iconSpacing": 4,
"activeColor": "primary",
"inactiveColor": "none",
"micFilterRegex": ""
}

View File

@ -86,6 +86,18 @@
"id": "Clock",
"tooltipFormat": "HH:mm ddd, MMM dd",
"useCustomFont": false
},
{
"defaultSettings": {
"activeColor": "primary",
"enableToast": true,
"hideInactive": false,
"iconSpacing": 4,
"inactiveColor": "none",
"micFilterRegex": "",
"removeMargins": false
},
"id": "plugin:privacy-indicator"
}
],
"left": [
@ -123,6 +135,13 @@
}
],
"right": [
{
"displayMode": "onhover",
"iconColor": "none",
"id": "Volume",
"middleClickCommand": "pwvucontrol || pavucontrol",
"textColor": "none"
},
{
"blacklist": [
],
@ -306,7 +325,7 @@
"autoStartAuth": false,
"avatarImage": "/home/user/.face",
"boxRadiusRatio": 1,
"clockFormat": "hh\\nmm",
"clockFormat": "HH:mm ddd MMM d ",
"clockStyle": "custom",
"compactLockScreen": false,
"dimmerOpacity": 0.23,
@ -349,13 +368,13 @@
],
"lockScreenTint": 0,
"passwordChars": false,
"radiusRatio": 1,
"radiusRatio": 0,
"reverseScroll": false,
"scaleRatio": 1,
"screenRadiusRatio": 1,
"shadowDirection": "bottom_right",
"shadowOffsetX": 2,
"shadowOffsetY": 3,
"shadowDirection": "center",
"shadowOffsetX": 0,
"shadowOffsetY": 0,
"showChangelogOnStartup": true,
"showHibernateOnLockScreen": false,
"showScreenCorners": false,
@ -379,15 +398,15 @@
"customCommands": "[]",
"enabled": true,
"fadeDuration": 5,
"lockCommand": "",
"lockTimeout": 660,
"lockCommand": "qs -c noctalia-shell ipc call session lock",
"lockTimeout": 300,
"resumeLockCommand": "",
"resumeScreenOffCommand": "",
"resumeScreenOffCommand": "swaymsg \"output * dpms on\"",
"resumeSuspendCommand": "",
"screenOffCommand": "",
"screenOffTimeout": 600,
"suspendCommand": "",
"suspendTimeout": 1800
"screenOffCommand": "swaymsg \"output * dpms off\"",
"screenOffTimeout": 360,
"suspendCommand": "systemctl suspend",
"suspendTimeout": 900
},
"location": {
"analogClockInCalendar": false,
@ -419,7 +438,7 @@
"autoSchedule": true,
"dayTemp": "6500",
"enabled": true,
"forced": false,
"forced": true,
"manualSunrise": "06:30",
"manualSunset": "18:30",
"nightTemp": "4000"
@ -435,7 +454,7 @@
"density": "default",
"enableBatteryToast": true,
"enableKeyboardLayoutToast": true,
"enableMarkdown": false,
"enableMarkdown": true,
"enableMediaToast": false,
"enabled": true,
"location": "top_right",
@ -479,7 +498,7 @@
"notifyUpdates": true
},
"sessionMenu": {
"countdownDuration": 10000,
"countdownDuration": 5000,
"enableCountdown": true,
"largeButtonsLayout": "single-row",
"largeButtonsStyle": true,
@ -571,6 +590,26 @@
},
"templates": {
"activeTemplates": [
{
"enabled": true,
"id": "gtk"
},
{
"enabled": true,
"id": "qt"
},
{
"enabled": true,
"id": "foot"
},
{
"enabled": true,
"id": "btop"
},
{
"enabled": true,
"id": "zathura"
}
],
"enableUserTheming": false
},
@ -580,10 +619,10 @@
"fontDefaultScale": 1,
"fontFixed": "monospace",
"fontFixedScale": 1,
"panelBackgroundOpacity": 0.93,
"panelBackgroundOpacity": 1,
"panelsAttachedToBar": true,
"scrollbarAlwaysVisible": true,
"settingsPanelMode": "attached",
"settingsPanelMode": "centered",
"settingsPanelSideBarCardStyle": false,
"tooltipsEnabled": true,
"translucentWidgets": false

View File

@ -1,54 +1,27 @@
# Variabili di base
set $mod Mod4
# set $left h
# set $down j
# set $up k
# set $right l
set $term foot
# Launcher: wofi (sostituisce wmenu-run)
# set $menu wofi -S drun -p Search -I -b -i
# Wallpaper e output
# set $wallpaper ~/Nextcloud/wallpapers/laptop/hiroishi_nagasai.png
# set $benq_wallpaper ~/Nextcloud/wallpapers/laptop/hiroishi_nagasai.png
set $laptop eDP-1
set $benq DP-9
# Color Scheme (Gruvbox)
set $gruvbox_bg_dark #282828
set $gruvbox_bg_light #504945
set $gruvbox_fg_dark #ebdbb2
set $gruvbox_fg_light #d5c4a1
set $gruvbox_red #cc241d
set $gruvbox_green #98971a
set $gruvbox_yellow #d79921
set $gruvbox_blue #458588
set $gruvbox_purple #b16286
set $gruvbox_aqua #689d6a
set $gruvbox_orange #d65d0e
# Impostazione del wallpaper e configurazione output
# output * bg $wallpaper fill
# Imposta il profilo ICC, risoluzione, posizione e scala per il display laptop
output $laptop color_profile icc ~/Documents/dotfiles/sway/FRAMEWORK_13_BOE_ICC/BOE_CQ_______NE135FBM_N41_03.icm
output $laptop resolution 2256x1504 position 0,0
output $laptop scale 1.3
# Configure BenQ display
# output $benq bg $benq_wallpaper fill
output $laptop color_profile icc ~/Documents/dotfiles/sway/BENQ_PD2706QN_ICC/'Display P3.icc'
output $benq resolution 2560x1440@99.990Hz position 0,0
output $laptop scale 1.3
# Gestione degli eventi del coperchio
bindswitch --reload --locked lid:on output $laptop disable
bindswitch --reload --locked lid:off output $laptop enable
bindswitch --reload --locked lid:on exec ~/Documents/dotfiles/sway/scripts/lid.sh on $laptop
bindswitch --reload --locked lid:off exec ~/Documents/dotfiles/sway/scripts/lid.sh off $laptop
# Configurazione dell'inattività: lock e DPMS
# Delegata a noctalia-shell (abilitalo nelle impostazioni di Noctalia -> Idle)
input type:touchpad {
dwt enabled # Disable while typing - RIATTIVA QUESTA OPZIONE
dwt disabled # Disable while typing - RIATTIVA QUESTA OPZIONE
tap enabled # Enable tap-to-click
natural_scroll enabled # Natural scrolling
middle_emulation enabled # Emulate middle mouse button - RIATTIVA QUESTA OPZIONE
@ -74,7 +47,7 @@ bindsym $mod+Return exec $term
# Chiudi finestra focalizzata
bindsym $mod+Shift+q kill
set $ipc qs -c noctalia-shell ipc call
set $ipc /usr/sbin/qs -c noctalia-shell ipc call
# Avvia il launcher
bindsym $mod+d exec $ipc launcher toggle
@ -166,9 +139,9 @@ bindsym --locked XF86MonBrightnessUp exec $ipc brightness increase
bindsym --locked XF86MonBrightnessDown exec $ipc brightness decrease
# Controlli multimediali
bindsym XF86AudioPlay exec ipc call media playPause
bindsym XF86AudioNext exec ipc call media next
bindsym XF86AudioPrev exec ipc call media previous
bindsym XF86AudioPlay exec $ipc media playPause
bindsym XF86AudioNext exec $ipc media next
bindsym XF86AudioPrev exec $ipc media previous
bindsym $mod+Shift+h exec handy --toggle-transcription
bindsym $mod+v exec $ipc launcher clipboard
@ -201,7 +174,7 @@ gaps outer 2
gaps inner 3
# Impostazioni dei colori per il focus delle finestre
client.focused $gruvbox_orange $gruvbox_orange $gruvbox_bg_dark $gruvbox_orange
# client.focused $gruvbox_orange $gruvbox_orange $gruvbox_bg_dark $gruvbox_orange
hide_edge_borders smart
### Impostazioni GTK per tema scuro
@ -246,16 +219,13 @@ exec dbus-update-activation-environment WAYLAND_DISPLAY DISPLAY XDG_CURRENT_DESK
### Notifiche Desktop e Clip History
# exec swaync
# exec mako
exec wl-paste --type text --watch cliphist store
exec wl-paste --type image --watch cliphist store
### Applet di rete
exec nm-applet --indicator
exec handy --start-hidden # speek to text
exec wl-paste --type text --watch cliphist store # Stores only text data
exec wl-paste --type image --watch cliphist store # Stores only image data
exec_always pkill qs; qs -d -c noctalia-shell
exec_always sh -c '/usr/bin/pkill -x qs || true; exec /usr/sbin/qs -d -c noctalia-shell'
# comando per capire come si chiama l'id della app per le regole delle finestre
# swaymsg -t get_tree
@ -280,3 +250,5 @@ for_window [title="Picture in picture"] floating enable, sticky enable
for_window [title="Save File"] floating enable
for_window [app_id="firefox" title="Firefox — Sharing Indicator"] kill
include ~/.config/sway/noctalia

18
sway/noctalia Normal file
View File

@ -0,0 +1,18 @@
set $primary #b8bb26
set $on_primary #282828
set $secondary #fabd2f
set $tertiary #83a598
set $error #fb4934
set $surface #282828
set $on_surface #fbf1c7
set $on_surface_variant #ebdbb2
set $outline #786f6b
## Window Colours
# class border backgr. text indicator child_border
client.focused $primary $surface $on_surface $primary $primary
client.focused_inactive $outline $surface $on_surface_variant $outline $outline
client.unfocused $outline $surface $on_surface_variant $outline $outline
client.urgent $error $surface $on_surface $error $error
client.placeholder $surface $surface $on_surface_variant $surface $surface
client.background $surface

25
sway/scripts/lid.sh Executable file
View File

@ -0,0 +1,25 @@
#!/bin/sh
action="$1"
internal="${2:-eDP-1}"
active_count="$(swaymsg -t get_outputs | python3 -c 'import json,sys; print(sum(1 for o in json.load(sys.stdin) if o.get("active")))' 2>/dev/null)"
if [ -z "$active_count" ]; then
active_count=1
fi
case "$action" in
on)
if [ "$active_count" -le 1 ]; then
qs -c noctalia-shell ipc call session lock
sleep 1
systemctl suspend
else
swaymsg "output $internal disable"
fi
;;
off)
swaymsg "output $internal enable"
;;
esac

31
zathura/noctaliarc Normal file
View File

@ -0,0 +1,31 @@
set default-bg "rgba(40, 40, 40, 0.8)"
set default-fg "#fbf1c7"
set recolor-lightcolor "rgba(0,0,0,0)"
set recolor-darkcolor "#fbf1c7"
set statusbar-bg "#282828"
set statusbar-fg "#b8bb26"
set inputbar-bg "#282828"
set inputbar-fg "#b8bb26"
set notification-bg "#282828"
set notification-fg "#b8bb26"
set notification-error-bg "#282828"
set notification-error-fg "#fb4934"
set notification-warning-bg "#f2f4be"
set notification-warning-fg "#7d0d00"
set index-bg "rgba(0,0,0,0)"
set index-fg "#fbf1c7"
set index-active-bg "#b8bb26"
set index-active-fg "#282828"
set highlight-color "rgba(242, 244, 190, 0.5)"
set highlight-active-color "rgba(232, 233, 149, 0.5)"
set highlight-fg "#282828"
set completion-bg "#282828"
set completion-fg "#b8bb26"
set completion-group-bg "#282828"
set completion-group-fg "#b8bb26"
set completion-highlight-fg "#282828"
set completion-highlight-bg "#b8bb26"