forked from Shinonome/dots-hyprland
work safety for clipboard images copied from browser
This commit is contained in:
@@ -42,7 +42,12 @@ Variants {
|
||||
// Wallpaper
|
||||
property bool wallpaperIsVideo: Config.options.background.wallpaperPath.endsWith(".mp4") || Config.options.background.wallpaperPath.endsWith(".webm") || Config.options.background.wallpaperPath.endsWith(".mkv") || Config.options.background.wallpaperPath.endsWith(".avi") || Config.options.background.wallpaperPath.endsWith(".mov")
|
||||
property string wallpaperPath: wallpaperIsVideo ? Config.options.background.thumbnailPath : Config.options.background.wallpaperPath
|
||||
property bool wallpaperSafetyTriggered: Config.options.background.wallpaperSafety.enable && (CF.StringUtils.stringListContainsSubstring(wallpaperPath.toLowerCase(), Config.options.background.wallpaperSafety.triggerCondition.wallpaperKeywords) && CF.StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.background.wallpaperSafety.triggerCondition.networkNameKeywords))
|
||||
property bool wallpaperSafetyTriggered: {
|
||||
const enabled = Config.options.workSafety.enable.wallpaper
|
||||
const sensitiveWallpaper = (CF.StringUtils.stringListContainsSubstring(wallpaperPath.toLowerCase(), Config.options.workSafety.triggerCondition.fileKeywords))
|
||||
const sensitiveNetwork = (CF.StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords))
|
||||
return enabled && sensitiveWallpaper && sensitiveNetwork;
|
||||
}
|
||||
property real wallpaperToScreenRatio: Math.min(wallpaperWidth / screen.width, wallpaperHeight / screen.height)
|
||||
property real preferredWallpaperScale: Config.options.background.parallax.workspaceZoom
|
||||
property real effectiveWallpaperScale: 1 // Some reasonable init value, to be updated
|
||||
|
||||
@@ -141,13 +141,6 @@ Singleton {
|
||||
property real workspaceZoom: 1.07 // Relative to your screen, not wallpaper size
|
||||
property bool enableSidebar: true
|
||||
}
|
||||
property JsonObject wallpaperSafety: JsonObject {
|
||||
property bool enable: true
|
||||
property JsonObject triggerCondition: JsonObject {
|
||||
property list<string> wallpaperKeywords: ["anime", "ecchi", "hentai", "yande.re", "konachan", "breast", "nipples", "pussy", "nsfw", "spoiler", "girl"]
|
||||
property list<string> networkNameKeywords: ["airport", "cafe", "college", "company", "eduroam", "free", "guest", "public", "school", "university"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject bar: JsonObject {
|
||||
@@ -379,6 +372,18 @@ Singleton {
|
||||
property JsonObject screenshotTool: JsonObject {
|
||||
property bool showContentRegions: true
|
||||
}
|
||||
|
||||
property JsonObject workSafety: JsonObject {
|
||||
property JsonObject enable: JsonObject {
|
||||
property bool wallpaper: true
|
||||
property bool clipboard: true
|
||||
}
|
||||
property JsonObject triggerCondition: JsonObject {
|
||||
property list<string> networkNameKeywords: ["airport", "cafe", "college", "company", "eduroam", "free", "guest", "public", "school", "university"]
|
||||
property list<string> fileKeywords: ["anime", "ecchi", "hentai", "yande.re", "konachan", "breast", "nipples", "pussy", "nsfw", "spoiler", "girl"]
|
||||
property list<string> linkKeywords: ["hentai", "porn", "sukebei", "hitomi.la", "rule34", "gelbooru", "fanbox", "dlsite"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ Rectangle {
|
||||
property string entry
|
||||
property real maxWidth
|
||||
property real maxHeight
|
||||
property bool blur: false
|
||||
property string blurText: "Image hidden"
|
||||
|
||||
property string imageDecodePath: Directories.cliphistDecode
|
||||
property string imageDecodeFileName: `${entryNumber}`
|
||||
@@ -19,26 +21,25 @@ Rectangle {
|
||||
property string source
|
||||
|
||||
property int entryNumber: {
|
||||
if (!root.entry) return 0
|
||||
const match = root.entry.match(/^(\d+)\t/)
|
||||
return match ? parseInt(match[1]) : 0
|
||||
if (!root.entry)
|
||||
return 0;
|
||||
const match = root.entry.match(/^(\d+)\t/);
|
||||
return match ? parseInt(match[1]) : 0;
|
||||
}
|
||||
property int imageWidth: {
|
||||
if (!root.entry) return 0
|
||||
const match = root.entry.match(/(\d+)x(\d+)/)
|
||||
return match ? parseInt(match[1]) : 0
|
||||
if (!root.entry)
|
||||
return 0;
|
||||
const match = root.entry.match(/(\d+)x(\d+)/);
|
||||
return match ? parseInt(match[1]) : 0;
|
||||
}
|
||||
property int imageHeight: {
|
||||
if (!root.entry) return 0
|
||||
const match = root.entry.match(/(\d+)x(\d+)/)
|
||||
return match ? parseInt(match[2]) : 0
|
||||
if (!root.entry)
|
||||
return 0;
|
||||
const match = root.entry.match(/(\d+)x(\d+)/);
|
||||
return match ? parseInt(match[2]) : 0;
|
||||
}
|
||||
property real scale: {
|
||||
return Math.min(
|
||||
root.maxWidth / imageWidth,
|
||||
root.maxHeight / imageHeight,
|
||||
1
|
||||
)
|
||||
return Math.min(root.maxWidth / imageWidth, root.maxHeight / imageHeight, 1);
|
||||
}
|
||||
|
||||
color: Appearance.colors.colLayer1
|
||||
@@ -47,26 +48,33 @@ Rectangle {
|
||||
implicitWidth: imageWidth * scale
|
||||
|
||||
Component.onCompleted: {
|
||||
decodeImageProcess.running = true
|
||||
decodeImageProcess.running = true;
|
||||
}
|
||||
|
||||
Process {
|
||||
id: decodeImageProcess
|
||||
command: ["bash", "-c",
|
||||
`[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(root.entry)}' | ${Cliphist.cliphistBinary} decode > '${imageDecodeFilePath}'`
|
||||
]
|
||||
command: ["bash", "-c", `[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(root.entry)}' | ${Cliphist.cliphistBinary} decode > '${imageDecodeFilePath}'`]
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
if (exitCode === 0) {
|
||||
root.source = imageDecodeFilePath
|
||||
root.source = imageDecodeFilePath;
|
||||
} else {
|
||||
console.error("[CliphistImage] Failed to decode image for entry:", root.entry)
|
||||
root.source = ""
|
||||
console.error("[CliphistImage] Failed to decode image for entry:", root.entry);
|
||||
root.source = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
Quickshell.execDetached(["bash", "-c", `[ -f '${imageDecodeFilePath}' ] && rm -f '${imageDecodeFilePath}'`])
|
||||
Quickshell.execDetached(["bash", "-c", `[ -f '${imageDecodeFilePath}' ] && rm -f '${imageDecodeFilePath}'`]);
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: image.width
|
||||
height: image.height
|
||||
radius: root.radius
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
@@ -82,15 +90,42 @@ Rectangle {
|
||||
height: root.imageHeight * root.scale
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: image.width
|
||||
height: image.height
|
||||
radius: root.radius
|
||||
Loader {
|
||||
id: blurLoader
|
||||
active: root.blur
|
||||
anchors.fill: image
|
||||
sourceComponent: GaussianBlur {
|
||||
source: image
|
||||
radius: 35
|
||||
samples: radius * 2 + 1
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: ColorUtils.transparentize(Appearance.colors.colLayer0, 0.5)
|
||||
|
||||
Column {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
MaterialSymbol {
|
||||
visible: width <= image.width
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: "visibility_off"
|
||||
font.pixelSize: 28
|
||||
}
|
||||
StyledText {
|
||||
visible: width <= image.width
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: root.blurText
|
||||
color: Appearance.colors.colOnSurface
|
||||
font.pixelSize: Appearance.font.pixelSize.smallie
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ RippleButton {
|
||||
property string bigText: entry?.bigText ?? ""
|
||||
property string materialSymbol: entry?.materialSymbol ?? ""
|
||||
property string cliphistRawString: entry?.cliphistRawString ?? ""
|
||||
property bool blurImage: entry?.blurImage ?? false
|
||||
property string blurImageText: entry?.blurImageText ?? "Image hidden"
|
||||
|
||||
visible: root.entryShown
|
||||
property int horizontalMargin: 10
|
||||
@@ -208,6 +210,8 @@ RippleButton {
|
||||
entry: root.cliphistRawString
|
||||
maxWidth: contentColumn.width
|
||||
maxHeight: 140
|
||||
blur: root.blurImage
|
||||
blurText: root.blurImageText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,20 +20,10 @@ Item { // Wrapper
|
||||
implicitHeight: searchWidgetContent.implicitHeight + Appearance.sizes.elevationMargin * 2
|
||||
|
||||
property string mathResult: ""
|
||||
|
||||
function disableExpandAnimation() {
|
||||
searchWidthBehavior.enabled = false;
|
||||
}
|
||||
|
||||
function cancelSearch() {
|
||||
searchInput.selectAll();
|
||||
root.searchingText = "";
|
||||
searchWidthBehavior.enabled = true;
|
||||
}
|
||||
|
||||
function setSearchingText(text) {
|
||||
searchInput.text = text;
|
||||
root.searchingText = text;
|
||||
property bool clipboardWorkSafetyActive: {
|
||||
const enabled = Config.options.workSafety.enable.clipboard;
|
||||
const sensitiveNetwork = (StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords))
|
||||
return enabled && sensitiveNetwork;
|
||||
}
|
||||
|
||||
property var searchActions: [
|
||||
@@ -97,6 +87,27 @@ Item { // Wrapper
|
||||
appResults.currentIndex = 0;
|
||||
}
|
||||
|
||||
function disableExpandAnimation() {
|
||||
searchWidthBehavior.enabled = false;
|
||||
}
|
||||
|
||||
function cancelSearch() {
|
||||
searchInput.selectAll();
|
||||
root.searchingText = "";
|
||||
searchWidthBehavior.enabled = true;
|
||||
}
|
||||
|
||||
function setSearchingText(text) {
|
||||
searchInput.text = text;
|
||||
root.searchingText = text;
|
||||
}
|
||||
|
||||
function containsUnsafeLink(entry) {
|
||||
if (entry == undefined) return false;
|
||||
const unsafeKeywords = Config.options.workSafety.triggerCondition.linkKeywords;
|
||||
return StringUtils.stringListContainsSubstring(entry.toLowerCase(), unsafeKeywords);
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: nonAppResultsTimer
|
||||
interval: Config.options.search.nonAppResultDelay
|
||||
@@ -311,7 +322,12 @@ Item { // Wrapper
|
||||
if (root.searchingText.startsWith(Config.options.search.prefix.clipboard)) {
|
||||
// Clipboard
|
||||
const searchString = root.searchingText.slice(Config.options.search.prefix.clipboard.length);
|
||||
return Cliphist.fuzzyQuery(searchString).map(entry => {
|
||||
return Cliphist.fuzzyQuery(searchString).map((entry, index, array) => {
|
||||
const mightBlurImage = Cliphist.entryIsImage(entry) && root.clipboardWorkSafetyActive;
|
||||
let shouldBlurImage = mightBlurImage;
|
||||
if (mightBlurImage) {
|
||||
shouldBlurImage = shouldBlurImage && (containsUnsafeLink(array[index - 1]) || containsUnsafeLink(array[index + 1]));
|
||||
}
|
||||
return {
|
||||
cliphistRawString: entry,
|
||||
name: StringUtils.cleanCliphistEntry(entry),
|
||||
@@ -335,7 +351,9 @@ Item { // Wrapper
|
||||
Cliphist.deleteEntry(entry);
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
blurImage: shouldBlurImage,
|
||||
blurImageText: Translation.tr("Work safety")
|
||||
};
|
||||
}).filter(Boolean);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user