forked from Shinonome/dots-hyprland
clipboard history: images
This commit is contained in:
@@ -26,15 +26,16 @@ Singleton {
|
|||||||
property string shellConfigName: "config.json"
|
property string shellConfigName: "config.json"
|
||||||
property string shellConfigPath: `${Directories.shellConfig}/${Directories.shellConfigName}`
|
property string shellConfigPath: `${Directories.shellConfig}/${Directories.shellConfigName}`
|
||||||
property string todoPath: FileUtils.trimFileProtocol(`${Directories.state}/user/todo.json`)
|
property string todoPath: FileUtils.trimFileProtocol(`${Directories.state}/user/todo.json`)
|
||||||
property string notificationsPath: `${Directories.cache}/notifications/notifications.json`
|
property string notificationsPath: FileUtils.trimFileProtocol(`${Directories.cache}/notifications/notifications.json`)
|
||||||
property string generatedMaterialThemePath: `${Directories.state}/user/generated/colors.json`
|
property string generatedMaterialThemePath: FileUtils.trimFileProtocol(`${Directories.state}/user/generated/colors.json`)
|
||||||
|
property string cliphistDecode: FileUtils.trimFileProtocol(`${Directories.cache}/media/cliphist`)
|
||||||
// Cleanup on init
|
// Cleanup on init
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
Hyprland.dispatch(`exec mkdir -p ${Directories.shellConfig}`)
|
Hyprland.dispatch(`exec mkdir -p '${favicons}'`)
|
||||||
Hyprland.dispatch(`exec mkdir -p ${favicons}`)
|
Hyprland.dispatch(`exec rm -rf '${coverArt}'; mkdir -p '${coverArt}'`)
|
||||||
Hyprland.dispatch(`exec rm -rf ${coverArt} && mkdir -p ${coverArt}`)
|
Hyprland.dispatch(`exec rm -rf '${booruPreviews}'; mkdir -p '${booruPreviews}'`)
|
||||||
Hyprland.dispatch(`exec rm -rf '${booruPreviews}' && mkdir -p '${booruPreviews}'`)
|
|
||||||
Hyprland.dispatch(`exec mkdir -p '${booruDownloads}' && mkdir -p '${booruDownloadsNsfw}'`)
|
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}'`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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:/"
|
||||||
|
import "root:/services"
|
||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
import QtQuick
|
import QtQuick
|
||||||
@@ -191,6 +192,7 @@ Scope {
|
|||||||
GlobalStates.overviewOpen = false;
|
GlobalStates.overviewOpen = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Cliphist.refresh()
|
||||||
for (let i = 0; i < overviewVariants.instances.length; i++) {
|
for (let i = 0; i < overviewVariants.instances.length; i++) {
|
||||||
let panelWindow = overviewVariants.instances[i];
|
let panelWindow = overviewVariants.instances[i];
|
||||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ RippleButton {
|
|||||||
property string fontType: entry?.fontType ?? "main"
|
property string fontType: entry?.fontType ?? "main"
|
||||||
property string itemClickActionName: entry?.clickActionName
|
property string itemClickActionName: entry?.clickActionName
|
||||||
property string materialSymbol: entry?.materialSymbol ?? ""
|
property string materialSymbol: entry?.materialSymbol ?? ""
|
||||||
|
property string cliphistRawString: entry?.cliphistRawString ?? ""
|
||||||
|
|
||||||
property string highlightPrefix: `<u><font color="${Appearance.m3colors.m3primary}">`
|
property string highlightPrefix: `<u><font color="${Appearance.m3colors.m3primary}">`
|
||||||
property string highlightSuffix: `</font></u>`
|
property string highlightSuffix: `</font></u>`
|
||||||
@@ -62,8 +63,8 @@ RippleButton {
|
|||||||
if (!root.itemName) return [];
|
if (!root.itemName) return [];
|
||||||
// Regular expression to match URLs
|
// Regular expression to match URLs
|
||||||
const urlRegex = /https?:\/\/[^\s<>"{}|\\^`[\]]+/gi;
|
const urlRegex = /https?:\/\/[^\s<>"{}|\\^`[\]]+/gi;
|
||||||
const matches = root.itemName.match(urlRegex)
|
const matches = root.itemName?.match(urlRegex)
|
||||||
.filter(url => !url.includes("…")) // Elided = invalid
|
?.filter(url => !url.includes("…")) // Elided = invalid
|
||||||
return matches ? matches : [];
|
return matches ? matches : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,6 +144,7 @@ RippleButton {
|
|||||||
|
|
||||||
// Main text
|
// Main text
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
id: contentColumn
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
spacing: 0
|
spacing: 0
|
||||||
@@ -173,6 +175,15 @@ RippleButton {
|
|||||||
text: `${root.displayContent}`
|
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
|
// Action text
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ Item { // Wrapper
|
|||||||
clip: true
|
clip: true
|
||||||
topMargin: 10
|
topMargin: 10
|
||||||
bottomMargin: 10
|
bottomMargin: 10
|
||||||
spacing: 0
|
spacing: 2
|
||||||
KeyNavigation.up: searchBar
|
KeyNavigation.up: searchBar
|
||||||
|
|
||||||
onFocusChanged: {
|
onFocusChanged: {
|
||||||
@@ -305,11 +305,13 @@ Item { // Wrapper
|
|||||||
const searchString = root.searchingText.slice(ConfigOptions.search.prefix.clipboard.length);
|
const searchString = root.searchingText.slice(ConfigOptions.search.prefix.clipboard.length);
|
||||||
return Cliphist.fuzzyQuery(searchString).map(entry => {
|
return Cliphist.fuzzyQuery(searchString).map(entry => {
|
||||||
return {
|
return {
|
||||||
|
cliphistRawString: entry,
|
||||||
name: entry.replace(/^\s*\S+\s+/, ""),
|
name: entry.replace(/^\s*\S+\s+/, ""),
|
||||||
clickActionName: qsTr("Copy"),
|
clickActionName: "",
|
||||||
type: `#${entry.match(/^\s*(\S+)/)?.[1] || ""}`,
|
type: `#${entry.match(/^\s*(\S+)/)?.[1] || ""}`,
|
||||||
execute: () => {
|
execute: () => {
|
||||||
Hyprland.dispatch(`exec echo '${StringUtils.shellSingleQuoteEscape(entry)}' | cliphist decode | wl-copy`);
|
Hyprland.dispatch(`exec echo '${StringUtils.shellSingleQuoteEscape(entry)}' | cliphist decode | wl-copy`);
|
||||||
|
Cliphist.refresh()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}).filter(Boolean);
|
}).filter(Boolean);
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ Singleton {
|
|||||||
|
|
||||||
function refresh() {
|
function refresh() {
|
||||||
readProc.buffer = []
|
readProc.buffer = []
|
||||||
readProc.running = false
|
|
||||||
readProc.running = true
|
readProc.running = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user