mirror of
https://github.com/danbulant/dots-hyprland
synced 2026-05-24 12:22:09 +00:00
clipboard history: images
This commit is contained in:
parent
f04d5f6202
commit
442ddc1a7b
6 changed files with 123 additions and 12 deletions
|
|
@ -26,15 +26,16 @@ Singleton {
|
|||
property string shellConfigName: "config.json"
|
||||
property string shellConfigPath: `${Directories.shellConfig}/${Directories.shellConfigName}`
|
||||
property string todoPath: FileUtils.trimFileProtocol(`${Directories.state}/user/todo.json`)
|
||||
property string notificationsPath: `${Directories.cache}/notifications/notifications.json`
|
||||
property string generatedMaterialThemePath: `${Directories.state}/user/generated/colors.json`
|
||||
property string notificationsPath: FileUtils.trimFileProtocol(`${Directories.cache}/notifications/notifications.json`)
|
||||
property string generatedMaterialThemePath: FileUtils.trimFileProtocol(`${Directories.state}/user/generated/colors.json`)
|
||||
property string cliphistDecode: FileUtils.trimFileProtocol(`${Directories.cache}/media/cliphist`)
|
||||
// Cleanup on init
|
||||
Component.onCompleted: {
|
||||
Hyprland.dispatch(`exec mkdir -p ${Directories.shellConfig}`)
|
||||
Hyprland.dispatch(`exec mkdir -p ${favicons}`)
|
||||
Hyprland.dispatch(`exec rm -rf ${coverArt} && mkdir -p ${coverArt}`)
|
||||
Hyprland.dispatch(`exec rm -rf '${booruPreviews}' && mkdir -p '${booruPreviews}'`)
|
||||
Hyprland.dispatch(`exec mkdir -p '${favicons}'`)
|
||||
Hyprland.dispatch(`exec rm -rf '${coverArt}'; mkdir -p '${coverArt}'`)
|
||||
Hyprland.dispatch(`exec rm -rf '${booruPreviews}'; mkdir -p '${booruPreviews}'`)
|
||||
Hyprland.dispatch(`exec mkdir -p '${booruDownloads}' && mkdir -p '${booruDownloadsNsfw}'`)
|
||||
Hyprland.dispatch(`exec rm -rf ${latexOutput} && mkdir -p ${latexOutput}`)
|
||||
Hyprland.dispatch(`exec rm -rf '${latexOutput}'; mkdir -p '${latexOutput}'`)
|
||||
Hyprland.dispatch(`exec rm -rf '${cliphistDecode}'; mkdir -p '${cliphistDecode}'`)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
96
.config/quickshell/modules/common/widgets/CliphistImage.qml
Normal file
96
.config/quickshell/modules/common/widgets/CliphistImage.qml
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/services"
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import "root:/modules/common/functions/file_utils.js" as FileUtils
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Qt.labs.platform
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Io
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property string entry
|
||||
property real maxWidth
|
||||
property real maxHeight
|
||||
|
||||
property string imageDecodePath: Directories.cliphistDecode
|
||||
property string imageDecodeFileName: `${entryNumber}`
|
||||
property string imageDecodeFilePath: `${imageDecodePath}/${imageDecodeFileName}`
|
||||
property string source
|
||||
|
||||
property int entryNumber: {
|
||||
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
|
||||
}
|
||||
property int imageHeight: {
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
color: Appearance.colors.colLayer1
|
||||
radius: Appearance.rounding.small
|
||||
implicitHeight: imageHeight * scale
|
||||
implicitWidth: imageWidth * scale
|
||||
|
||||
Component.onCompleted: {
|
||||
decodeImageProcess.running = true
|
||||
}
|
||||
|
||||
Process {
|
||||
id: decodeImageProcess
|
||||
command: ["bash", "-c",
|
||||
`[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(root.entry)}' | cliphist decode > '${imageDecodeFilePath}'`
|
||||
]
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
if (exitCode === 0) {
|
||||
root.source = imageDecodeFilePath
|
||||
} else {
|
||||
console.error("[CliphistImage] Failed to decode image for entry:", root.entry)
|
||||
root.source = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: image
|
||||
anchors.fill: parent
|
||||
|
||||
source: Qt.resolvedUrl(root.source)
|
||||
fillMode: Image.PreserveAspectFit
|
||||
antialiasing: true
|
||||
asynchronous: true
|
||||
|
||||
width: root.imageWidth * root.scale
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import "root:/"
|
||||
import "root:/services"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import QtQuick
|
||||
|
|
@ -191,6 +192,7 @@ Scope {
|
|||
GlobalStates.overviewOpen = false;
|
||||
return;
|
||||
}
|
||||
Cliphist.refresh()
|
||||
for (let i = 0; i < overviewVariants.instances.length; i++) {
|
||||
let panelWindow = overviewVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ RippleButton {
|
|||
property string fontType: entry?.fontType ?? "main"
|
||||
property string itemClickActionName: entry?.clickActionName
|
||||
property string materialSymbol: entry?.materialSymbol ?? ""
|
||||
property string cliphistRawString: entry?.cliphistRawString ?? ""
|
||||
|
||||
property string highlightPrefix: `<u><font color="${Appearance.m3colors.m3primary}">`
|
||||
property string highlightSuffix: `</font></u>`
|
||||
|
|
@ -62,8 +63,8 @@ RippleButton {
|
|||
if (!root.itemName) return [];
|
||||
// Regular expression to match URLs
|
||||
const urlRegex = /https?:\/\/[^\s<>"{}|\\^`[\]]+/gi;
|
||||
const matches = root.itemName.match(urlRegex)
|
||||
.filter(url => !url.includes("…")) // Elided = invalid
|
||||
const matches = root.itemName?.match(urlRegex)
|
||||
?.filter(url => !url.includes("…")) // Elided = invalid
|
||||
return matches ? matches : [];
|
||||
}
|
||||
|
||||
|
|
@ -143,6 +144,7 @@ RippleButton {
|
|||
|
||||
// Main text
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: 0
|
||||
|
|
@ -173,6 +175,15 @@ RippleButton {
|
|||
text: `${root.displayContent}`
|
||||
}
|
||||
}
|
||||
Loader {
|
||||
active: root.cliphistRawString && /^\d+\t\[\[.*binary data.*\d+x\d+.*\]\]$/.test(root.cliphistRawString)
|
||||
sourceComponent: CliphistImage {
|
||||
Layout.fillWidth: true
|
||||
entry: root.cliphistRawString
|
||||
maxWidth: contentColumn.width
|
||||
maxHeight: 140
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Action text
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ Item { // Wrapper
|
|||
clip: true
|
||||
topMargin: 10
|
||||
bottomMargin: 10
|
||||
spacing: 0
|
||||
spacing: 2
|
||||
KeyNavigation.up: searchBar
|
||||
|
||||
onFocusChanged: {
|
||||
|
|
@ -305,11 +305,13 @@ Item { // Wrapper
|
|||
const searchString = root.searchingText.slice(ConfigOptions.search.prefix.clipboard.length);
|
||||
return Cliphist.fuzzyQuery(searchString).map(entry => {
|
||||
return {
|
||||
cliphistRawString: entry,
|
||||
name: entry.replace(/^\s*\S+\s+/, ""),
|
||||
clickActionName: qsTr("Copy"),
|
||||
clickActionName: "",
|
||||
type: `#${entry.match(/^\s*(\S+)/)?.[1] || ""}`,
|
||||
execute: () => {
|
||||
Hyprland.dispatch(`exec echo '${StringUtils.shellSingleQuoteEscape(entry)}' | cliphist decode | wl-copy`);
|
||||
Cliphist.refresh()
|
||||
}
|
||||
};
|
||||
}).filter(Boolean);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ Singleton {
|
|||
|
||||
function refresh() {
|
||||
readProc.buffer = []
|
||||
readProc.running = false
|
||||
readProc.running = true
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue